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);
1355 MEMBDUP(extradata->resent_from);
1358 refs = msginfo->references;
1359 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1360 newmsginfo->references = g_slist_prepend
1361 (newmsginfo->references, g_strdup(refs->data));
1363 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1366 MEMBDUP(plaintext_file);
1371 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1373 MsgInfo *full_msginfo;
1375 if (msginfo == NULL) return NULL;
1377 if (!file || !is_file_exist(file)) {
1378 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1382 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1383 if (!full_msginfo) return NULL;
1385 msginfo->total_size = full_msginfo->total_size;
1386 msginfo->planned_download = full_msginfo->planned_download;
1388 if (full_msginfo->extradata) {
1389 if (!msginfo->extradata)
1390 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1391 if (!msginfo->extradata->list_post)
1392 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1393 if (!msginfo->extradata->list_subscribe)
1394 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1395 if (!msginfo->extradata->list_unsubscribe)
1396 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1397 if (!msginfo->extradata->list_help)
1398 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1399 if (!msginfo->extradata->list_archive)
1400 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1401 if (!msginfo->extradata->list_owner)
1402 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1403 if (!msginfo->extradata->avatars)
1404 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1405 (GCopyFunc) procmsg_msginfoavatar_copy);
1406 if (!msginfo->extradata->dispositionnotificationto)
1407 msginfo->extradata->dispositionnotificationto =
1408 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1409 if (!msginfo->extradata->returnreceiptto)
1410 msginfo->extradata->returnreceiptto = g_strdup
1411 (full_msginfo->extradata->returnreceiptto);
1412 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1413 msginfo->extradata->partial_recv = g_strdup
1414 (full_msginfo->extradata->partial_recv);
1415 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1416 msginfo->extradata->account_server = g_strdup
1417 (full_msginfo->extradata->account_server);
1418 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1419 msginfo->extradata->account_login = g_strdup
1420 (full_msginfo->extradata->account_login);
1421 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1422 msginfo->extradata->resent_from = g_strdup
1423 (full_msginfo->extradata->resent_from);
1425 procmsg_msginfo_free(full_msginfo);
1427 return procmsg_msginfo_new_ref(msginfo);
1430 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1432 MsgInfo *full_msginfo;
1435 if (msginfo == NULL) return NULL;
1437 file = procmsg_get_message_file_path(msginfo);
1438 if (!file || !is_file_exist(file)) {
1440 file = procmsg_get_message_file(msginfo);
1442 if (!file || !is_file_exist(file)) {
1443 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1447 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1449 return full_msginfo;
1452 void procmsg_msginfo_free(MsgInfo *msginfo)
1454 if (msginfo == NULL) return;
1457 if (msginfo->refcnt > 0)
1460 if (msginfo->to_folder) {
1461 msginfo->to_folder->op_count--;
1462 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1465 g_free(msginfo->fromspace);
1467 g_free(msginfo->fromname);
1469 g_free(msginfo->date);
1470 g_free(msginfo->from);
1471 g_free(msginfo->to);
1472 g_free(msginfo->cc);
1473 g_free(msginfo->newsgroups);
1474 g_free(msginfo->subject);
1475 g_free(msginfo->msgid);
1476 g_free(msginfo->inreplyto);
1477 g_free(msginfo->xref);
1479 if (msginfo->extradata) {
1480 if (msginfo->extradata->avatars) {
1481 g_slist_foreach(msginfo->extradata->avatars,
1482 (GFunc)procmsg_msginfoavatar_free,
1484 g_slist_free(msginfo->extradata->avatars);
1486 g_free(msginfo->extradata->returnreceiptto);
1487 g_free(msginfo->extradata->dispositionnotificationto);
1488 g_free(msginfo->extradata->list_post);
1489 g_free(msginfo->extradata->list_subscribe);
1490 g_free(msginfo->extradata->list_unsubscribe);
1491 g_free(msginfo->extradata->list_help);
1492 g_free(msginfo->extradata->list_archive);
1493 g_free(msginfo->extradata->list_owner);
1494 g_free(msginfo->extradata->partial_recv);
1495 g_free(msginfo->extradata->account_server);
1496 g_free(msginfo->extradata->account_login);
1497 g_free(msginfo->extradata->resent_from);
1498 g_free(msginfo->extradata);
1500 slist_free_strings_full(msginfo->references);
1501 g_slist_free(msginfo->tags);
1503 g_free(msginfo->plaintext_file);
1508 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1513 memusage += sizeof(MsgInfo);
1514 if (msginfo->fromname)
1515 memusage += strlen(msginfo->fromname);
1517 memusage += strlen(msginfo->date);
1519 memusage += strlen(msginfo->from);
1521 memusage += strlen(msginfo->to);
1523 memusage += strlen(msginfo->cc);
1524 if (msginfo->newsgroups)
1525 memusage += strlen(msginfo->newsgroups);
1526 if (msginfo->subject)
1527 memusage += strlen(msginfo->subject);
1529 memusage += strlen(msginfo->msgid);
1530 if (msginfo->inreplyto)
1531 memusage += strlen(msginfo->inreplyto);
1533 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1534 gchar *r = (gchar *)tmp->data;
1535 memusage += r?strlen(r):0 + sizeof(GSList);
1537 if (msginfo->fromspace)
1538 memusage += strlen(msginfo->fromspace);
1540 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1541 memusage += sizeof(GSList);
1543 if (msginfo->extradata) {
1544 memusage += sizeof(MsgInfoExtraData);
1545 if (msginfo->extradata->avatars) {
1546 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1547 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1548 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1549 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1552 if (msginfo->extradata->dispositionnotificationto)
1553 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1554 if (msginfo->extradata->returnreceiptto)
1555 memusage += strlen(msginfo->extradata->returnreceiptto);
1557 if (msginfo->extradata->partial_recv)
1558 memusage += strlen(msginfo->extradata->partial_recv);
1559 if (msginfo->extradata->account_server)
1560 memusage += strlen(msginfo->extradata->account_server);
1561 if (msginfo->extradata->account_login)
1562 memusage += strlen(msginfo->extradata->account_login);
1563 if (msginfo->extradata->resent_from)
1564 memusage += strlen(msginfo->extradata->resent_from);
1566 if (msginfo->extradata->list_post)
1567 memusage += strlen(msginfo->extradata->list_post);
1568 if (msginfo->extradata->list_subscribe)
1569 memusage += strlen(msginfo->extradata->list_subscribe);
1570 if (msginfo->extradata->list_unsubscribe)
1571 memusage += strlen(msginfo->extradata->list_unsubscribe);
1572 if (msginfo->extradata->list_help)
1573 memusage += strlen(msginfo->extradata->list_help);
1574 if (msginfo->extradata->list_archive)
1575 memusage += strlen(msginfo->extradata->list_archive);
1576 if (msginfo->extradata->list_owner)
1577 memusage += strlen(msginfo->extradata->list_owner);
1582 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1583 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1585 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1586 {"SSV:", NULL, FALSE},
1587 {"R:", NULL, FALSE},
1588 {"NG:", NULL, FALSE},
1589 {"MAID:", NULL, FALSE},
1590 {"NAID:", NULL, FALSE},
1591 {"SCF:", NULL, FALSE},
1592 {"RMID:", NULL, FALSE},
1593 {"FMID:", NULL, FALSE},
1594 {"X-Claws-Privacy-System:", NULL, FALSE},
1595 {"X-Claws-Encrypt:", NULL, FALSE},
1596 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1597 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1598 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1599 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1600 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1601 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1602 {NULL, NULL, FALSE}};
1605 gint mailval = 0, newsval = 0;
1607 gchar *smtpserver = NULL;
1608 GSList *to_list = NULL;
1609 GSList *newsgroup_list = NULL;
1610 gchar *savecopyfolder = NULL;
1611 gchar *replymessageid = NULL;
1612 gchar *fwdmessageid = NULL;
1613 gchar *privacy_system = NULL;
1614 gboolean encrypt = FALSE;
1615 gchar *encrypt_data = NULL;
1616 gchar buf[BUFFSIZE];
1618 PrefsAccount *mailac = NULL, *newsac = NULL;
1619 gboolean save_clear_text = TRUE;
1620 gchar *tmp_enc_file = NULL;
1622 cm_return_val_if_fail(file != NULL, -1);
1624 if ((fp = g_fopen(file, "rb")) == NULL) {
1625 FILE_OP_ERROR(file, "fopen");
1627 if (*errstr) g_free(*errstr);
1628 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1633 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1635 gchar *p = buf + strlen(qentry[hnum].name);
1643 if (smtpserver == NULL)
1644 smtpserver = g_strdup(p);
1647 to_list = address_list_append(to_list, p);
1650 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1652 case Q_MAIL_ACCOUNT_ID:
1653 mailac = account_find_from_id(atoi(p));
1655 case Q_NEWS_ACCOUNT_ID:
1656 newsac = account_find_from_id(atoi(p));
1658 case Q_SAVE_COPY_FOLDER:
1659 if (savecopyfolder == NULL)
1660 savecopyfolder = g_strdup(p);
1662 case Q_REPLY_MESSAGE_ID:
1663 if (replymessageid == NULL)
1664 replymessageid = g_strdup(p);
1666 case Q_FWD_MESSAGE_ID:
1667 if (fwdmessageid == NULL)
1668 fwdmessageid = g_strdup(p);
1670 case Q_PRIVACY_SYSTEM:
1671 case Q_PRIVACY_SYSTEM_OLD:
1672 if (privacy_system == NULL)
1673 privacy_system = g_strdup(p);
1680 case Q_ENCRYPT_DATA:
1681 case Q_ENCRYPT_DATA_OLD:
1682 if (encrypt_data == NULL)
1683 encrypt_data = g_strdup(p);
1686 case Q_CLAWS_HDRS_OLD:
1687 /* end of special headers reached */
1688 goto send_mail; /* can't "break;break;" */
1692 filepos = ftell(fp);
1697 if (mailac && mailac->save_encrypted_as_clear_text
1698 && !mailac->encrypt_to_self)
1699 save_clear_text = TRUE;
1701 save_clear_text = FALSE;
1706 mimeinfo = procmime_scan_queue_file(file);
1707 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1708 || (fp = my_tmpfile()) == NULL
1709 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1712 procmime_mimeinfo_free_all(mimeinfo);
1715 slist_free_strings_full(to_list);
1716 slist_free_strings_full(newsgroup_list);
1717 g_free(savecopyfolder);
1718 g_free(replymessageid);
1719 g_free(fwdmessageid);
1720 g_free(privacy_system);
1721 g_free(encrypt_data);
1723 if (*errstr) g_free(*errstr);
1724 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1725 privacy_get_error());
1731 if (!save_clear_text) {
1732 gchar *content = NULL;
1733 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1737 content = file_read_stream_to_str(fp);
1740 str_write_to_file(content, tmp_enc_file);
1743 g_warning("couldn't get tempfile\n");
1747 procmime_mimeinfo_free_all(mimeinfo);
1753 debug_print("Sending message by mail\n");
1756 if (*errstr) g_free(*errstr);
1757 *errstr = g_strdup_printf(_("Queued message header is broken."));
1760 } else if (mailac && mailac->use_mail_command &&
1761 mailac->mail_command && (* mailac->mail_command)) {
1762 mailval = send_message_local(mailac->mail_command, fp);
1765 mailac = account_find_from_smtp_server(from, smtpserver);
1767 g_warning("Account not found. "
1768 "Using current account...\n");
1769 mailac = cur_account;
1774 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1775 if (mailval == -1 && errstr) {
1776 if (*errstr) g_free(*errstr);
1777 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1780 PrefsAccount tmp_ac;
1782 g_warning("Account not found.\n");
1784 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1785 tmp_ac.address = from;
1786 tmp_ac.smtp_server = smtpserver;
1787 tmp_ac.smtpport = SMTP_PORT;
1788 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1789 if (mailval == -1 && errstr) {
1790 if (*errstr) g_free(*errstr);
1791 *errstr = g_strdup_printf(_("No specific account has been found to "
1792 "send, and an error happened during SMTP session."));
1796 } else if (!to_list && !newsgroup_list) {
1798 if (*errstr) g_free(*errstr);
1799 *errstr = g_strdup(_("Couldn't determine sending information. "
1800 "Maybe the email hasn't been generated by Claws Mail."));
1805 fseek(fp, filepos, SEEK_SET);
1806 if (newsgroup_list && newsac && (mailval == 0)) {
1811 /* write to temporary file */
1812 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1813 G_DIR_SEPARATOR, file);
1814 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1815 FILE_OP_ERROR(tmp, "fopen");
1817 alertpanel_error(_("Couldn't create temporary file for news sending."));
1819 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1820 FILE_OP_ERROR(tmp, "chmod");
1821 g_warning("can't change file mode\n");
1824 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1825 if (fputs(buf, tmpfp) == EOF) {
1826 FILE_OP_ERROR(tmp, "fputs");
1829 if (*errstr) g_free(*errstr);
1830 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1837 debug_print("Sending message by news\n");
1839 folder = FOLDER(newsac->folder);
1841 newsval = news_post(folder, tmp);
1842 if (newsval < 0 && errstr) {
1843 if (*errstr) g_free(*errstr);
1844 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1845 newsac->nntp_server);
1855 /* update session statistics */
1856 if (mailval == 0 && newsval == 0) {
1857 /* update session stats */
1859 session_stats.replied++;
1860 else if (fwdmessageid)
1861 session_stats.forwarded++;
1863 session_stats.sent++;
1866 /* save message to outbox */
1867 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1870 debug_print("saving sent message...\n");
1872 outbox = folder_find_item_from_identifier(savecopyfolder);
1874 outbox = folder_get_default_outbox();
1876 if (save_clear_text || tmp_enc_file == NULL) {
1877 gboolean saved = FALSE;
1878 *queued_removed = FALSE;
1879 if (queue && msgnum > 0) {
1880 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1881 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1882 debug_print("moved queued mail %d to sent folder\n", msgnum);
1884 *queued_removed = TRUE;
1885 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1886 debug_print("copied queued mail %d to sent folder\n", msgnum);
1889 procmsg_msginfo_free(queued_mail);
1892 debug_print("resaving clear text queued mail to sent folder\n");
1893 procmsg_save_to_outbox(outbox, file, TRUE);
1896 debug_print("saving encrpyted queued mail to sent folder\n");
1897 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1901 if (tmp_enc_file != NULL) {
1902 claws_unlink(tmp_enc_file);
1904 tmp_enc_file = NULL;
1907 if (replymessageid != NULL || fwdmessageid != NULL) {
1911 if (replymessageid != NULL)
1912 tokens = g_strsplit(replymessageid, "\t", 0);
1914 tokens = g_strsplit(fwdmessageid, "\t", 0);
1915 item = folder_find_item_from_identifier(tokens[0]);
1917 /* check if queued message has valid folder and message id */
1918 if (item != NULL && tokens[2] != NULL) {
1921 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1923 /* check if referring message exists and has a message id */
1924 if ((msginfo != NULL) &&
1925 (msginfo->msgid != NULL) &&
1926 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1927 procmsg_msginfo_free(msginfo);
1931 if (msginfo == NULL) {
1932 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1935 if (msginfo != NULL) {
1936 if (replymessageid != NULL) {
1937 MsgPermFlags to_unset = 0;
1939 if (prefs_common.mark_as_read_on_new_window)
1940 to_unset = (MSG_NEW|MSG_UNREAD);
1942 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1943 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1945 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1947 procmsg_msginfo_free(msginfo);
1955 slist_free_strings_full(to_list);
1956 slist_free_strings_full(newsgroup_list);
1957 g_free(savecopyfolder);
1958 g_free(replymessageid);
1959 g_free(fwdmessageid);
1960 g_free(privacy_system);
1961 g_free(encrypt_data);
1963 return (newsval != 0 ? newsval : mailval);
1966 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1968 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1969 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1970 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1974 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1977 if (procmsg_queue_lock(errstr)) {
1978 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1979 procmsg_queue_unlock();
1985 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1987 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1990 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1994 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1999 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
2000 item->unread_msgs++;
2001 if (procmsg_msg_has_marked_parent(msginfo))
2002 item->unreadmarked_msgs++;
2005 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
2006 item->unread_msgs--;
2007 if (procmsg_msg_has_marked_parent(msginfo))
2008 item->unreadmarked_msgs--;
2012 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
2013 procmsg_update_unread_children(msginfo, TRUE);
2014 item->marked_msgs++;
2017 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
2018 procmsg_update_unread_children(msginfo, FALSE);
2019 item->marked_msgs--;
2022 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
2023 item->replied_msgs++;
2026 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
2027 item->replied_msgs--;
2030 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
2031 item->forwarded_msgs++;
2034 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
2035 item->forwarded_msgs--;
2038 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
2039 item->locked_msgs++;
2042 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
2043 item->locked_msgs--;
2046 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
2047 item->ignored_msgs--;
2050 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
2051 item->ignored_msgs++;
2054 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
2055 item->watched_msgs--;
2058 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2059 item->watched_msgs++;
2063 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2066 MsgInfoUpdate msginfo_update;
2067 MsgPermFlags perm_flags_new, perm_flags_old;
2068 MsgTmpFlags tmp_flags_old;
2070 cm_return_if_fail(msginfo != NULL);
2071 item = msginfo->folder;
2072 cm_return_if_fail(item != NULL);
2074 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2076 /* Perm Flags handling */
2077 perm_flags_old = msginfo->flags.perm_flags;
2078 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2079 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2080 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2082 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2083 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2086 if (perm_flags_old != perm_flags_new) {
2087 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2089 update_folder_msg_counts(item, msginfo, perm_flags_old);
2090 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2093 /* Tmp flags handling */
2094 tmp_flags_old = msginfo->flags.tmp_flags;
2095 msginfo->flags.tmp_flags |= tmp_flags;
2097 /* update notification */
2098 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2099 msginfo_update.msginfo = msginfo;
2100 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2101 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2102 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2106 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2109 MsgInfoUpdate msginfo_update;
2110 MsgPermFlags perm_flags_new, perm_flags_old;
2111 MsgTmpFlags tmp_flags_old;
2113 cm_return_if_fail(msginfo != NULL);
2114 item = msginfo->folder;
2115 cm_return_if_fail(item != NULL);
2117 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2119 /* Perm Flags handling */
2120 perm_flags_old = msginfo->flags.perm_flags;
2121 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2123 if (perm_flags_old != perm_flags_new) {
2124 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2126 update_folder_msg_counts(item, msginfo, perm_flags_old);
2129 /* Tmp flags hanlding */
2130 tmp_flags_old = msginfo->flags.tmp_flags;
2131 msginfo->flags.tmp_flags &= ~tmp_flags;
2133 /* update notification */
2134 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2135 msginfo_update.msginfo = msginfo;
2136 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2137 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2138 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2142 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2143 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2144 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2147 MsgInfoUpdate msginfo_update;
2148 MsgPermFlags perm_flags_new, perm_flags_old;
2149 MsgTmpFlags tmp_flags_old;
2151 cm_return_if_fail(msginfo != NULL);
2152 item = msginfo->folder;
2153 cm_return_if_fail(item != NULL);
2155 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2157 /* Perm Flags handling */
2158 perm_flags_old = msginfo->flags.perm_flags;
2159 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2160 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2161 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2163 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2164 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2167 if (perm_flags_old != perm_flags_new) {
2168 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2170 update_folder_msg_counts(item, msginfo, perm_flags_old);
2174 /* Tmp flags handling */
2175 tmp_flags_old = msginfo->flags.tmp_flags;
2176 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2177 msginfo->flags.tmp_flags |= add_tmp_flags;
2179 /* update notification */
2180 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2181 msginfo_update.msginfo = msginfo;
2182 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2183 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2184 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2189 *\brief check for flags (e.g. mark) in prior msgs of current thread
2191 *\param info Current message
2192 *\param perm_flags Flags to be checked
2193 *\param parentmsgs Hash of prior msgs to avoid loops
2195 *\return gboolean TRUE if perm_flags are found
2197 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2198 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2202 cm_return_val_if_fail(info != NULL, FALSE);
2204 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2205 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2207 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2208 procmsg_msginfo_free(tmp);
2210 } else if (tmp != NULL) {
2213 if (g_hash_table_lookup(parentmsgs, info)) {
2214 debug_print("loop detected: %d\n",
2218 g_hash_table_insert(parentmsgs, info, "1");
2219 result = procmsg_msg_has_flagged_parent_real(
2220 tmp, perm_flags, parentmsgs);
2222 procmsg_msginfo_free(tmp);
2232 *\brief Callback for cleaning up hash of parentmsgs
2234 static gboolean parentmsgs_hash_remove(gpointer key,
2242 *\brief Set up list of parentmsgs
2243 * See procmsg_msg_has_flagged_parent_real()
2245 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2248 static GHashTable *parentmsgs = NULL;
2250 if (parentmsgs == NULL)
2251 parentmsgs = g_hash_table_new(NULL, NULL);
2253 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2254 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2260 *\brief Check if msgs prior in thread are marked
2261 * See procmsg_msg_has_flagged_parent_real()
2263 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2265 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2269 static GSList *procmsg_find_children_func(MsgInfo *info,
2270 GSList *children, GSList *all)
2274 cm_return_val_if_fail(info!=NULL, children);
2275 if (info->msgid == NULL)
2278 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2279 MsgInfo *tmp = (MsgInfo *)cur->data;
2280 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2281 /* Check if message is already in the list */
2282 if ((children == NULL) ||
2283 (g_slist_index(children, tmp) == -1)) {
2284 children = g_slist_prepend(children,
2285 procmsg_msginfo_new_ref(tmp));
2286 children = procmsg_find_children_func(tmp,
2295 static GSList *procmsg_find_children (MsgInfo *info)
2300 cm_return_val_if_fail(info!=NULL, NULL);
2301 all = folder_item_get_msg_list(info->folder);
2302 children = procmsg_find_children_func(info, NULL, all);
2303 if (children != NULL) {
2304 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2305 /* this will not free the used pointers
2306 created with procmsg_msginfo_new_ref */
2307 procmsg_msginfo_free((MsgInfo *)cur->data);
2315 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2317 GSList *children = procmsg_find_children(info);
2319 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2320 MsgInfo *tmp = (MsgInfo *)cur->data;
2321 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2323 info->folder->unreadmarked_msgs++;
2325 info->folder->unreadmarked_msgs--;
2326 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2328 procmsg_msginfo_free(tmp);
2330 g_slist_free(children);
2334 * Set the destination folder for a copy or move operation
2336 * \param msginfo The message which's destination folder is changed
2337 * \param to_folder The destination folder for the operation
2339 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2341 if(msginfo->to_folder != NULL) {
2342 msginfo->to_folder->op_count--;
2343 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2345 msginfo->to_folder = to_folder;
2346 if(to_folder != NULL) {
2347 to_folder->op_count++;
2348 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2353 * Apply filtering actions to the msginfo
2355 * \param msginfo The MsgInfo describing the message that should be filtered
2356 * \return TRUE if the message was moved and MsgInfo is now invalid,
2359 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2361 MailFilteringData mail_filtering_data;
2363 mail_filtering_data.msginfo = msginfo;
2364 mail_filtering_data.msglist = NULL;
2365 mail_filtering_data.filtered = NULL;
2366 mail_filtering_data.unfiltered = NULL;
2367 mail_filtering_data.account = ac_prefs;
2369 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2370 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2373 /* filter if enabled in prefs or move to inbox if not */
2374 if((filtering_rules != NULL) &&
2375 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2376 FILTERING_INCORPORATION, NULL)) {
2383 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2384 GSList **filtered, GSList **unfiltered,
2387 GSList *cur, *to_do = NULL;
2388 gint total = 0, curnum = 0;
2389 MailFilteringData mail_filtering_data;
2391 cm_return_if_fail(filtered != NULL);
2392 cm_return_if_fail(unfiltered != NULL);
2400 total = g_slist_length(list);
2404 *unfiltered = g_slist_copy(list);
2408 statusbar_print_all(_("Filtering messages...\n"));
2410 mail_filtering_data.msginfo = NULL;
2411 mail_filtering_data.msglist = list;
2412 mail_filtering_data.filtered = NULL;
2413 mail_filtering_data.unfiltered = NULL;
2414 mail_filtering_data.account = ac;
2416 if (!ac || ac->filterhook_on_recv)
2417 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2419 if (mail_filtering_data.filtered == NULL &&
2420 mail_filtering_data.unfiltered == NULL) {
2421 /* nothing happened */
2422 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2425 if (mail_filtering_data.filtered != NULL) {
2426 /* keep track of what's been filtered by the hooks */
2427 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2428 g_slist_length(list),
2429 g_slist_length(mail_filtering_data.filtered),
2430 g_slist_length(mail_filtering_data.unfiltered));
2432 *filtered = g_slist_copy(mail_filtering_data.filtered);
2434 if (mail_filtering_data.unfiltered != NULL) {
2435 /* what the hooks didn't handle will go in filtered or
2436 * unfiltered in the next loop */
2437 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2438 g_slist_length(list),
2439 g_slist_length(mail_filtering_data.filtered),
2440 g_slist_length(mail_filtering_data.unfiltered));
2441 to_do = mail_filtering_data.unfiltered;
2444 for (cur = to_do; cur; cur = cur->next) {
2445 MsgInfo *info = (MsgInfo *)cur->data;
2446 if (procmsg_msginfo_filter(info, ac))
2447 *filtered = g_slist_prepend(*filtered, info);
2449 *unfiltered = g_slist_prepend(*unfiltered, info);
2450 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2453 g_slist_free(mail_filtering_data.filtered);
2454 g_slist_free(mail_filtering_data.unfiltered);
2456 *filtered = g_slist_reverse(*filtered);
2457 *unfiltered = g_slist_reverse(*unfiltered);
2459 statusbar_progress_all(0,0,0);
2460 statusbar_pop_all();
2463 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2465 MsgInfo *tmp_msginfo = NULL;
2466 MsgFlags flags = {0, 0};
2467 gchar *tmpfile = get_tmp_file();
2468 FILE *fp = g_fopen(tmpfile, "wb");
2470 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2471 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2472 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2479 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2482 tmp_msginfo = procheader_parse_file(
2489 if (tmp_msginfo != NULL) {
2491 tmp_msginfo->folder = src_msginfo->folder;
2492 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2494 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2502 static GSList *spam_learners = NULL;
2504 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2506 if (!g_slist_find(spam_learners, learn_func))
2507 spam_learners = g_slist_append(spam_learners, learn_func);
2508 if (mainwindow_get_mainwindow()) {
2509 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2510 summary_set_menu_sensitive(
2511 mainwindow_get_mainwindow()->summaryview);
2512 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2516 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2518 spam_learners = g_slist_remove(spam_learners, learn_func);
2519 if (mainwindow_get_mainwindow()) {
2520 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2521 summary_set_menu_sensitive(
2522 mainwindow_get_mainwindow()->summaryview);
2523 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2527 gboolean procmsg_spam_can_learn(void)
2529 return g_slist_length(spam_learners) > 0;
2532 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2534 GSList *cur = spam_learners;
2536 for (; cur; cur = cur->next) {
2537 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2538 ret |= func(info, list, spam);
2543 static gchar *spam_folder_item = NULL;
2544 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2545 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2547 g_free(spam_folder_item);
2548 if (item_identifier)
2549 spam_folder_item = g_strdup(item_identifier);
2551 spam_folder_item = NULL;
2552 if (spam_get_folder_func != NULL)
2553 procmsg_spam_get_folder_func = spam_get_folder_func;
2555 procmsg_spam_get_folder_func = NULL;
2558 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2560 FolderItem *item = NULL;
2562 if (procmsg_spam_get_folder_func)
2563 item = procmsg_spam_get_folder_func(msginfo);
2564 if (item == NULL && spam_folder_item)
2565 item = folder_find_item_from_identifier(spam_folder_item);
2567 item = folder_get_default_trash();
2571 static void item_has_queued_mails(FolderItem *item, gpointer data)
2573 gboolean *result = (gboolean *)data;
2574 if (*result == TRUE)
2576 if (folder_has_parent_of_type(item, F_QUEUE)) {
2577 if (item->total_msgs == 0)
2580 GSList *msglist = folder_item_get_msg_list(item);
2582 for (cur = msglist; cur; cur = cur->next) {
2583 MsgInfo *msginfo = (MsgInfo *)cur->data;
2584 if (!MSG_IS_DELETED(msginfo->flags) &&
2585 !MSG_IS_LOCKED(msginfo->flags)) {
2590 procmsg_msg_list_free(msglist);
2595 gboolean procmsg_have_queued_mails_fast (void)
2597 gboolean result = FALSE;
2598 folder_func_to_all_folders(item_has_queued_mails, &result);
2602 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2604 gboolean *result = (gboolean *)data;
2605 if (*result == TRUE)
2607 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2611 gboolean procmsg_have_trashed_mails_fast (void)
2613 gboolean result = FALSE;
2614 folder_func_to_all_folders(item_has_trashed_mails, &result);
2618 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2626 if (msginfo->tags == NULL)
2628 for (cur = msginfo->tags; cur; cur = cur->next) {
2629 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2633 tags = g_strdup(tag);
2635 int olen = strlen(tags);
2636 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2637 tags = g_realloc(tags, nlen+1);
2640 strcpy(tags+olen, ", ");
2641 strcpy(tags+olen+2, tag);
2648 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2656 msginfo->tags = g_slist_remove(
2658 GINT_TO_POINTER(id));
2659 changed.data = GINT_TO_POINTER(id);
2660 changed.next = NULL;
2661 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2663 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2664 msginfo->tags = g_slist_append(
2666 GINT_TO_POINTER(id));
2668 changed.data = GINT_TO_POINTER(id);
2669 changed.next = NULL;
2670 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2675 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2677 GSList *unset = msginfo->tags;
2678 msginfo->tags = NULL;
2679 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2680 g_slist_free(unset);