2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 the Claws Mail team and Hiroyuki Yamamoto
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>
32 #include "procheader.h"
33 #include "send_message.h"
35 #include "statusbar.h"
36 #include "prefs_filtering.h"
37 #include "filtering.h"
39 #include "foldersel.h"
40 #include "prefs_common.h"
42 #include "alertpanel.h"
46 #include "partial_download.h"
47 #include "mainwindow.h"
48 #include "summaryview.h"
54 #include "file-utils.h"
56 extern SessionStats session_stats
;
58 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
59 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
);
60 static void procmsg_update_unread_children (MsgInfo
*info
,
61 gboolean newly_marked
);
68 Q_MAIL_ACCOUNT_ID
= 4,
69 Q_NEWS_ACCOUNT_ID
= 5,
70 Q_SAVE_COPY_FOLDER
= 6,
71 Q_REPLY_MESSAGE_ID
= 7,
77 Q_PRIVACY_SYSTEM_OLD
= 13,
79 Q_ENCRYPT_DATA_OLD
= 15,
80 Q_CLAWS_HDRS_OLD
= 16,
83 void procmsg_msg_list_free(GSList
*mlist
)
88 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
89 msginfo
= (MsgInfo
*)cur
->data
;
90 procmsg_msginfo_free(&msginfo
);
95 MsgNumberList
*procmsg_get_number_list_for_msgs(MsgInfoList
*msglist
)
100 for (cur
= msglist
; cur
; cur
= cur
->next
) {
101 MsgInfo
*msg
= (MsgInfo
*)cur
->data
;
102 nums
= g_slist_prepend(nums
, GUINT_TO_POINTER(msg
->msgnum
));
105 return g_slist_reverse(nums
);
117 /* CLAWS subject threading:
119 in the first round it inserts subject lines in a
120 hashtable (subject <-> node)
122 the second round finishes the threads by attaching
123 matching subject lines to the one found in the
124 hashtable. will use the oldest node with the same
125 subject that is not more then thread_by_subject_max_age
126 days old (see subject_hashtable_lookup)
129 static void subject_hashtable_insert(GHashTable
*hashtable
, GNode
*node
)
135 cm_return_if_fail(hashtable
!= NULL
);
136 cm_return_if_fail(node
!= NULL
);
137 msginfo
= (MsgInfo
*) node
->data
;
138 cm_return_if_fail(msginfo
!= NULL
);
140 subject
= msginfo
->subject
;
144 subject
+= subject_get_prefix_length(subject
);
146 list
= g_hash_table_lookup(hashtable
, subject
);
147 list
= g_slist_prepend(list
, node
);
148 g_hash_table_insert(hashtable
, subject
, list
);
151 static GNode
*subject_hashtable_lookup(GHashTable
*hashtable
, MsgInfo
*msginfo
)
155 GNode
*node
= NULL
, *hashtable_node
= NULL
;
157 MsgInfo
*hashtable_msginfo
= NULL
, *best_msginfo
= NULL
;
160 cm_return_val_if_fail(hashtable
!= NULL
, NULL
);
162 subject
= msginfo
->subject
;
165 prefix_length
= subject_get_prefix_length(subject
);
166 if (prefix_length
<= 0)
168 subject
+= prefix_length
;
170 list
= g_hash_table_lookup(hashtable
, subject
);
174 /* check all nodes with the same subject to find the best parent */
175 for (cur
= list
; cur
; cur
= cur
->next
) {
176 hashtable_node
= (GNode
*)cur
->data
;
177 hashtable_msginfo
= (MsgInfo
*) hashtable_node
->data
;
180 /* best node should be the oldest in the found nodes */
181 /* parent node must not be older then msginfo */
182 if ((hashtable_msginfo
->date_t
< msginfo
->date_t
) &&
183 ((best_msginfo
== NULL
) ||
184 (best_msginfo
->date_t
> hashtable_msginfo
->date_t
)))
187 /* parent node must not be more then thread_by_subject_max_age
188 days older then msginfo */
189 if (fabs(difftime(msginfo
->date_t
, hashtable_msginfo
->date_t
)) >
190 prefs_common
.thread_by_subject_max_age
* 3600 * 24)
193 /* can add new tests for all matching
194 nodes found by subject */
197 node
= hashtable_node
;
198 best_msginfo
= hashtable_msginfo
;
205 static void subject_hashtable_free(gpointer key
, gpointer value
, gpointer data
)
210 /* return the reversed thread tree */
211 GNode
*procmsg_get_thread_tree(GSList
*mlist
)
213 GNode
*root
, *parent
, *node
, *next
;
214 GHashTable
*msgid_table
;
215 GHashTable
*subject_hashtable
= NULL
;
220 root
= g_node_new(NULL
);
221 msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
223 if (prefs_common
.thread_by_subject
) {
224 subject_hashtable
= g_hash_table_new(g_str_hash
, g_str_equal
);
227 for (; mlist
!= NULL
; mlist
= mlist
->next
) {
228 msginfo
= (MsgInfo
*)mlist
->data
;
231 if (msginfo
->inreplyto
) {
232 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
233 if (parent
== NULL
) {
237 node
= g_node_insert_data_before
238 (parent
, parent
== root
? parent
->children
: NULL
,
240 if ((msgid
= msginfo
->msgid
) && g_hash_table_lookup(msgid_table
, msgid
) == NULL
)
241 g_hash_table_insert(msgid_table
, (gchar
*)msgid
, node
);
243 /* CLAWS: add subject to hashtable (without prefix) */
244 if (prefs_common
.thread_by_subject
) {
245 subject_hashtable_insert(subject_hashtable
, node
);
249 /* complete the unfinished threads */
250 for (node
= root
->children
; node
!= NULL
; ) {
252 msginfo
= (MsgInfo
*)node
->data
;
255 if (msginfo
->inreplyto
)
256 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
258 /* try looking for the indirect parent */
259 if (!parent
&& msginfo
->references
) {
260 for (reflist
= msginfo
->references
;
261 reflist
!= NULL
; reflist
= reflist
->next
)
262 if ((parent
= g_hash_table_lookup
263 (msgid_table
, reflist
->data
)) != NULL
)
267 /* node should not be the parent, and node should not
268 be an ancestor of parent (circular reference) */
269 if (parent
&& parent
!= node
&&
270 !g_node_is_ancestor(node
, parent
)) {
273 (parent
, parent
->children
, node
);
279 if (prefs_common
.thread_by_subject
) {
280 START_TIMING("thread by subject");
281 for (node
= root
->children
; node
&& node
!= NULL
;) {
283 msginfo
= (MsgInfo
*) node
->data
;
285 parent
= subject_hashtable_lookup(subject_hashtable
, msginfo
);
287 /* the node may already be threaded by IN-REPLY-TO, so go up
289 find the parent node */
290 if (parent
!= NULL
) {
291 if (g_node_is_ancestor(node
, parent
))
299 g_node_append(parent
, node
);
307 if (prefs_common
.thread_by_subject
)
309 g_hash_table_foreach(subject_hashtable
, subject_hashtable_free
, NULL
);
310 g_hash_table_destroy(subject_hashtable
);
313 g_hash_table_destroy(msgid_table
);
318 gint
procmsg_move_messages(GSList
*mlist
)
320 GSList
*cur
, *movelist
= NULL
;
322 FolderItem
*dest
= NULL
;
324 gboolean finished
= TRUE
;
325 if (!mlist
) return 0;
327 folder_item_update_freeze();
330 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
331 msginfo
= (MsgInfo
*)cur
->data
;
332 if (!msginfo
->to_folder
) {
338 dest
= msginfo
->to_folder
;
339 movelist
= g_slist_prepend(movelist
, msginfo
);
340 } else if (dest
== msginfo
->to_folder
) {
341 movelist
= g_slist_prepend(movelist
, msginfo
);
345 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
348 movelist
= g_slist_reverse(movelist
);
349 retval
|= folder_item_move_msgs(dest
, movelist
);
350 g_slist_free(movelist
);
353 if (finished
== FALSE
) {
359 folder_item_update_thaw();
363 void procmsg_copy_messages(GSList
*mlist
)
365 GSList
*cur
, *copylist
= NULL
;
367 FolderItem
*dest
= NULL
;
368 gboolean finished
= TRUE
;
371 folder_item_update_freeze();
374 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
375 msginfo
= (MsgInfo
*)cur
->data
;
376 if (!msginfo
->to_folder
) {
382 dest
= msginfo
->to_folder
;
383 copylist
= g_slist_prepend(copylist
, msginfo
);
384 } else if (dest
== msginfo
->to_folder
) {
385 copylist
= g_slist_prepend(copylist
, msginfo
);
389 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
392 copylist
= g_slist_reverse(copylist
);
393 folder_item_copy_msgs(dest
, copylist
);
394 g_slist_free(copylist
);
397 if (finished
== FALSE
) {
403 folder_item_update_thaw();
406 gchar
*procmsg_get_message_file_path(MsgInfo
*msginfo
)
408 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
410 return folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
413 gchar
*procmsg_get_message_file(MsgInfo
*msginfo
)
415 gchar
*filename
= NULL
;
417 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
419 filename
= folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
421 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
426 gchar
*procmsg_get_message_file_full(MsgInfo
*msginfo
, gboolean headers
, gboolean body
)
428 gchar
*filename
= NULL
;
430 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
432 filename
= folder_item_fetch_msg_full(msginfo
->folder
, msginfo
->msgnum
,
435 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
440 GSList
*procmsg_get_message_file_list(GSList
*mlist
)
442 GSList
*file_list
= NULL
;
444 MsgFileInfo
*fileinfo
;
447 while (mlist
!= NULL
) {
448 msginfo
= (MsgInfo
*)mlist
->data
;
449 file
= procmsg_get_message_file(msginfo
);
451 procmsg_message_file_list_free(file_list
);
454 fileinfo
= g_new(MsgFileInfo
, 1);
455 fileinfo
->msginfo
= procmsg_msginfo_new_ref(msginfo
);
456 fileinfo
->file
= file
;
457 fileinfo
->flags
= g_new(MsgFlags
, 1);
458 *fileinfo
->flags
= msginfo
->flags
;
459 file_list
= g_slist_prepend(file_list
, fileinfo
);
463 file_list
= g_slist_reverse(file_list
);
468 void procmsg_message_file_list_free(MsgInfoList
*file_list
)
471 MsgFileInfo
*fileinfo
;
473 for (cur
= file_list
; cur
!= NULL
; cur
= cur
->next
) {
474 fileinfo
= (MsgFileInfo
*)cur
->data
;
475 procmsg_msginfo_free(&(fileinfo
->msginfo
));
476 g_free(fileinfo
->file
);
477 g_free(fileinfo
->flags
);
481 g_slist_free(file_list
);
484 FILE *procmsg_open_message(MsgInfo
*msginfo
, gboolean skip_special_headers
)
489 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
491 file
= procmsg_get_message_file_path(msginfo
);
492 cm_return_val_if_fail(file
!= NULL
, NULL
);
494 if (!is_file_exist(file
)) {
496 file
= procmsg_get_message_file(msginfo
);
501 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
502 FILE_OP_ERROR(file
, "claws_fopen");
509 if (MSG_IS_QUEUED(msginfo
->flags
) || MSG_IS_DRAFT(msginfo
->flags
) ||
510 skip_special_headers
== TRUE
) {
513 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
515 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
516 strlen("X-Claws-End-Special-Headers:"))) ||
517 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
518 strlen("X-Sylpheed-End-Special-Headers:"))))
521 if (buf
[0] == '\r' || buf
[0] == '\n') break;
522 /* from other mailers */
523 if (!strncmp(buf
, "Date: ", 6)
524 || !strncmp(buf
, "To: ", 4)
525 || !strncmp(buf
, "From: ", 6)
526 || !strncmp(buf
, "Subject: ", 9)) {
536 gboolean
procmsg_msg_exist(MsgInfo
*msginfo
)
540 if (!msginfo
) return FALSE
;
542 ret
= !folder_item_is_msg_changed(msginfo
->folder
, msginfo
);
547 void procmsg_get_filter_keyword(MsgInfo
*msginfo
, gchar
**header
, gchar
**key
,
548 PrefsFilterType type
)
550 static HeaderEntry hentry
[] = {{"X-BeenThere:", NULL
, TRUE
},
551 {"X-ML-Name:", NULL
, TRUE
},
552 {"X-List:", NULL
, TRUE
},
553 {"X-Mailing-list:", NULL
, TRUE
},
554 {"List-Id:", NULL
, TRUE
},
555 {"X-Sequence:", NULL
, TRUE
},
556 {"Sender:", NULL
, TRUE
},
557 {"List-Post:", NULL
, TRUE
},
558 {NULL
, NULL
, FALSE
}};
564 H_X_MAILING_LIST
= 3,
573 cm_return_if_fail(msginfo
!= NULL
);
574 cm_return_if_fail(header
!= NULL
);
575 cm_return_if_fail(key
!= NULL
);
584 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
)
586 procheader_get_header_fields(fp
, hentry
);
589 #define SET_FILTER_KEY(hstr, idx) \
591 *header = g_strdup(hstr); \
592 *key = hentry[idx].body; \
593 hentry[idx].body = NULL; \
596 if (hentry
[H_LIST_ID
].body
!= NULL
) {
597 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID
);
598 extract_list_id_str(*key
);
599 } else if (hentry
[H_X_BEENTHERE
].body
!= NULL
) {
600 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE
);
601 } else if (hentry
[H_X_ML_NAME
].body
!= NULL
) {
602 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME
);
603 } else if (hentry
[H_X_LIST
].body
!= NULL
) {
604 SET_FILTER_KEY("header \"X-List\"", H_X_LIST
);
605 } else if (hentry
[H_X_MAILING_LIST
].body
!= NULL
) {
606 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST
);
607 } else if (hentry
[H_X_SEQUENCE
].body
!= NULL
) {
610 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE
);
613 while (*p
!= '\0' && !g_ascii_isspace(*p
)) p
++;
614 while (g_ascii_isspace(*p
)) p
++;
615 if (g_ascii_isdigit(*p
)) {
621 } else if (hentry
[H_SENDER
].body
!= NULL
) {
622 SET_FILTER_KEY("header \"Sender\"", H_SENDER
);
623 } else if (hentry
[H_LIST_POST
].body
!= NULL
) {
624 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST
);
625 } else if (msginfo
->to
) {
626 *header
= g_strdup("to");
627 *key
= g_strdup(msginfo
->to
);
628 } else if (msginfo
->subject
) {
629 *header
= g_strdup("subject");
630 *key
= g_strdup(msginfo
->subject
);
633 g_free(hentry
[H_X_BEENTHERE
].body
);
634 hentry
[H_X_BEENTHERE
].body
= NULL
;
635 g_free(hentry
[H_X_ML_NAME
].body
);
636 hentry
[H_X_ML_NAME
].body
= NULL
;
637 g_free(hentry
[H_X_LIST
].body
);
638 hentry
[H_X_LIST
].body
= NULL
;
639 g_free(hentry
[H_X_MAILING_LIST
].body
);
640 hentry
[H_X_MAILING_LIST
].body
= NULL
;
641 g_free(hentry
[H_LIST_ID
].body
);
642 hentry
[H_LIST_ID
].body
= NULL
;
643 g_free(hentry
[H_SENDER
].body
);
644 hentry
[H_SENDER
].body
= NULL
;
645 g_free(hentry
[H_LIST_POST
].body
);
646 hentry
[H_LIST_POST
].body
= NULL
;
650 *header
= g_strdup("from");
651 *key
= g_strdup(msginfo
->from
);
654 *header
= g_strdup("to");
655 *key
= g_strdup(msginfo
->to
);
657 case FILTER_BY_SUBJECT
:
658 *header
= g_strdup("subject");
659 *key
= g_strdup(msginfo
->subject
);
661 case FILTER_BY_SENDER
:
662 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
)
664 procheader_get_header_fields(fp
, hentry
);
667 if (hentry
[H_SENDER
].body
!= NULL
)
668 SET_FILTER_KEY("header \"Sender\"", H_SENDER
);
670 g_free(hentry
[H_X_BEENTHERE
].body
);
671 hentry
[H_X_BEENTHERE
].body
= NULL
;
672 g_free(hentry
[H_X_ML_NAME
].body
);
673 hentry
[H_X_ML_NAME
].body
= NULL
;
674 g_free(hentry
[H_X_LIST
].body
);
675 hentry
[H_X_LIST
].body
= NULL
;
676 g_free(hentry
[H_X_MAILING_LIST
].body
);
677 hentry
[H_X_MAILING_LIST
].body
= NULL
;
678 g_free(hentry
[H_LIST_ID
].body
);
679 hentry
[H_LIST_ID
].body
= NULL
;
680 g_free(hentry
[H_SENDER
].body
);
681 hentry
[H_SENDER
].body
= NULL
;
683 #undef SET_FILTER_KEY
690 static void procmsg_empty_trash(FolderItem
*trash
)
695 (trash
->stype
!= F_TRASH
&&
696 !folder_has_parent_of_type(trash
, F_TRASH
)))
699 if (trash
&& trash
->total_msgs
> 0) {
700 GSList
*mlist
= folder_item_get_msg_list(trash
);
702 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
703 MsgInfo
* msginfo
= (MsgInfo
*) cur
->data
;
704 if (MSG_IS_LOCKED(msginfo
->flags
)) {
705 procmsg_msginfo_free(&msginfo
);
708 if (msginfo
->total_size
!= 0 &&
709 msginfo
->size
!= (off_t
)msginfo
->total_size
)
710 partial_mark_for_delete(msginfo
);
712 procmsg_msginfo_free(&msginfo
);
715 folder_item_remove_all_msg(trash
);
718 if (!trash
->node
|| !trash
->node
->children
)
721 node
= trash
->node
->children
;
722 while (node
!= NULL
) {
724 procmsg_empty_trash(FOLDER_ITEM(node
->data
));
729 void procmsg_empty_all_trash(void)
734 for (cur
= folder_get_list(); cur
!= NULL
; cur
= cur
->next
) {
735 Folder
*folder
= FOLDER(cur
->data
);
736 trash
= folder
->trash
;
737 procmsg_empty_trash(trash
);
738 if (folder
->account
&& folder
->account
->set_trash_folder
&&
739 folder_find_item_from_identifier(folder
->account
->trash_folder
))
741 folder_find_item_from_identifier(folder
->account
->trash_folder
));
745 static PrefsAccount
*procmsg_get_account_from_file(const gchar
*file
)
747 PrefsAccount
*mailac
= NULL
;
751 static HeaderEntry qentry
[] = {{"S:", NULL
, FALSE
},
752 {"SSV:", NULL
, FALSE
},
754 {"NG:", NULL
, FALSE
},
755 {"MAID:", NULL
, FALSE
},
756 {"NAID:", NULL
, FALSE
},
757 {"SCF:", NULL
, FALSE
},
758 {"RMID:", NULL
, FALSE
},
759 {"FMID:", NULL
, FALSE
},
760 {"X-Claws-Privacy-System:", NULL
, FALSE
},
761 {"X-Claws-Encrypt:", NULL
, FALSE
},
762 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
763 {"X-Claws-End-Special-Headers", NULL
, FALSE
},
764 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
765 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
766 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
},
767 {NULL
, NULL
, FALSE
}};
769 cm_return_val_if_fail(file
!= NULL
, NULL
);
771 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
772 FILE_OP_ERROR(file
, "claws_fopen");
776 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
777 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
779 if (hnum
== Q_MAIL_ACCOUNT_ID
) {
780 mailac
= account_find_from_id(atoi(p
));
792 gchar
*procmsg_msginfo_get_avatar(MsgInfo
*msginfo
, gint type
)
796 if (!msginfo
|| !msginfo
->extradata
|| !msginfo
->extradata
->avatars
)
799 for (mia
= msginfo
->extradata
->avatars
; mia
; mia
= mia
->next
) {
800 MsgInfoAvatar
*avatar
= (MsgInfoAvatar
*)mia
->data
;
801 if (avatar
->avatar_id
== type
)
802 return avatar
->avatar_src
;
808 void procmsg_msginfo_add_avatar(MsgInfo
*msginfo
, gint type
, const gchar
*data
)
812 if (!msginfo
->extradata
)
813 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
815 av
= g_new0(MsgInfoAvatar
, 1);
816 av
->avatar_id
= type
;
817 av
->avatar_src
= g_strdup(data
);
819 msginfo
->extradata
->avatars
= g_slist_append(msginfo
->extradata
->avatars
, av
);
822 gchar
*procmsg_msginfo_get_identifier(MsgInfo
*msginfo
)
828 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
829 folder_id
= folder_item_get_identifier(msginfo
->folder
);
830 msgid
= msginfo
->msgid
;
832 id
= g_strconcat(folder_id
, G_DIR_SEPARATOR_S
, msgid
, NULL
);
839 MsgInfo
*procmsg_get_msginfo_from_identifier(const gchar
*id
)
841 gchar
*folder_id
= g_strdup(id
);
842 gchar
*separator
= strrchr(folder_id
, G_DIR_SEPARATOR
);
847 if (separator
== NULL
) {
853 msgid
= separator
+ 1;
855 item
= folder_find_item_from_identifier(folder_id
);
862 msginfo
= folder_item_get_msginfo_by_msgid(item
, msgid
);
868 static GSList
*procmsg_list_sort_by_account(FolderItem
*queue
, GSList
*list
)
870 GSList
*result
= NULL
;
872 PrefsAccount
*last_account
= NULL
;
875 gboolean nothing_to_sort
= TRUE
;
880 orig
= g_slist_copy(list
);
882 msg
= (MsgInfo
*)orig
->data
;
884 for (cur
= orig
; cur
; cur
= cur
->next
)
885 debug_print("sort before %s\n", ((MsgInfo
*)cur
->data
)->from
);
890 nothing_to_sort
= TRUE
;
894 PrefsAccount
*ac
= NULL
;
895 msg
= (MsgInfo
*)cur
->data
;
896 file
= folder_item_fetch_msg(queue
, msg
->msgnum
);
897 ac
= procmsg_get_account_from_file(file
);
900 if (last_account
== NULL
|| (ac
!= NULL
&& ac
== last_account
)) {
901 result
= g_slist_append(result
, msg
);
902 orig
= g_slist_remove(orig
, msg
);
904 nothing_to_sort
= FALSE
;
910 if (orig
&& g_slist_length(orig
)) {
911 if (!last_account
&& nothing_to_sort
) {
912 /* can't find an account for the rest of the list */
915 result
= g_slist_append(result
, cur
->data
);
926 for (cur
= result
; cur
; cur
= cur
->next
)
927 debug_print("sort after %s\n", ((MsgInfo
*)cur
->data
)->from
);
934 static gboolean
procmsg_is_last_for_account(FolderItem
*queue
, MsgInfo
*msginfo
, GSList
*elem
)
936 gchar
*file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
937 PrefsAccount
*ac
= procmsg_get_account_from_file(file
);
940 for (cur
= elem
; cur
; cur
= cur
->next
) {
941 MsgInfo
*cur_msginfo
= (MsgInfo
*)cur
->data
;
942 file
= folder_item_fetch_msg(queue
, cur_msginfo
->msgnum
);
944 if (cur_msginfo
!= msginfo
&& !MSG_IS_LOCKED(cur_msginfo
->flags
)) {
945 if (procmsg_get_account_from_file(file
) == ac
) {
956 static gboolean send_queue_lock
= FALSE
;
958 gboolean
procmsg_queue_lock(char **errstr
)
960 if (send_queue_lock
) {
961 /* Avoid having to translate two similar strings */
962 log_warning(LOG_PROTOCOL
, "%s\n", _("Already trying to send."));
964 if (*errstr
) g_free(*errstr
);
965 *errstr
= g_strdup_printf(_("Already trying to send."));
969 send_queue_lock
= TRUE
;
972 void procmsg_queue_unlock(void)
974 send_queue_lock
= FALSE
;
977 *\brief Send messages in queue
979 *\param queue Queue folder to process
980 *\param save_msgs Unused
982 *\return Number of messages sent, negative if an error occurred
983 * positive if no error occurred
985 gint
procmsg_send_queue(FolderItem
*queue
, gboolean save_msgs
, gchar
**errstr
)
987 gint sent
= 0, err
= 0;
989 GSList
*sorted_list
= NULL
;
992 if (!procmsg_queue_lock(errstr
)) {
993 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
994 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
999 queue
= folder_get_default_queue();
1001 if (queue
== NULL
) {
1002 procmsg_queue_unlock();
1007 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1008 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1010 folder_item_scan(queue
);
1011 list
= folder_item_get_msg_list(queue
);
1013 /* sort the list per sender account; this helps reusing the same SMTP server */
1014 sorted_list
= procmsg_list_sort_by_account(queue
, list
);
1016 for (elem
= sorted_list
; elem
!= NULL
; elem
= elem
->next
) {
1020 msginfo
= (MsgInfo
*)(elem
->data
);
1021 if (!MSG_IS_LOCKED(msginfo
->flags
) && !MSG_IS_DELETED(msginfo
->flags
)) {
1022 file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
1024 gboolean queued_removed
= FALSE
;
1025 if (procmsg_send_message_queue_full(file
,
1026 !procmsg_is_last_for_account(queue
, msginfo
, elem
),
1027 errstr
, queue
, msginfo
->msgnum
, &queued_removed
) < 0) {
1028 g_warning("sending queued message %d failed",
1033 if (!queued_removed
)
1034 folder_item_remove_msg(queue
, msginfo
->msgnum
);
1039 /* FIXME: supposedly if only one message is locked, and queue
1040 * is being flushed, the following free says something like
1041 * "freeing msg ## in folder (nil)". */
1042 procmsg_msginfo_free(&msginfo
);
1045 g_slist_free(sorted_list
);
1046 folder_item_scan(queue
);
1048 if (queue
->node
&& queue
->node
->children
) {
1049 node
= queue
->node
->children
;
1050 while (node
!= NULL
) {
1053 send_queue_lock
= FALSE
;
1054 res
= procmsg_send_queue(FOLDER_ITEM(node
->data
), save_msgs
, errstr
);
1055 send_queue_lock
= TRUE
;
1063 procmsg_queue_unlock();
1065 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1066 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1068 return (err
!= 0 ? -err
: sent
);
1071 gboolean
procmsg_is_sending(void)
1073 return send_queue_lock
;
1077 *\brief Determine if a queue folder is empty
1079 *\param queue Queue folder to process
1081 *\return TRUE if the queue folder is empty, otherwise return FALSE
1083 gboolean
procmsg_queue_is_empty(FolderItem
*queue
)
1086 gboolean res
= FALSE
;
1088 queue
= folder_get_default_queue();
1089 cm_return_val_if_fail(queue
!= NULL
, TRUE
);
1091 folder_item_scan(queue
);
1092 list
= folder_item_get_msg_list(queue
);
1093 res
= (list
== NULL
);
1094 procmsg_msg_list_free(list
);
1098 if (queue
->node
&& queue
->node
->children
) {
1099 node
= queue
->node
->children
;
1100 while (node
!= NULL
) {
1102 if (!procmsg_queue_is_empty(FOLDER_ITEM(node
->data
)))
1111 gint
procmsg_remove_special_headers(const gchar
*in
, const gchar
*out
)
1114 gchar buf
[BUFFSIZE
];
1116 if ((fp
= claws_fopen(in
, "rb")) == NULL
) {
1117 FILE_OP_ERROR(in
, "claws_fopen");
1120 if ((outfp
= claws_fopen(out
, "wb")) == NULL
) {
1121 FILE_OP_ERROR(out
, "claws_fopen");
1125 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1127 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
1128 strlen("X-Claws-End-Special-Headers:"))) ||
1129 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
1130 strlen("X-Sylpheed-End-Special-Headers:"))))
1133 if (buf
[0] == '\r' || buf
[0] == '\n') break;
1134 /* from other mailers */
1135 if (!strncmp(buf
, "Date: ", 6)
1136 || !strncmp(buf
, "To: ", 4)
1137 || !strncmp(buf
, "From: ", 6)
1138 || !strncmp(buf
, "Subject: ", 9)) {
1143 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1144 if (claws_fputs(buf
, outfp
) == EOF
) {
1145 FILE_OP_ERROR(out
, "claws_fputs");
1146 claws_fclose(outfp
);
1151 claws_safe_fclose(outfp
);
1156 gint
procmsg_save_to_outbox(FolderItem
*outbox
, const gchar
*file
)
1159 MsgInfo
*msginfo
, *tmp_msginfo
;
1160 MsgFlags flag
= {0, 0};
1161 gchar
*outbox_path
= NULL
;
1164 debug_print("using default outbox\n");
1165 outbox
= folder_get_default_outbox();
1168 cm_return_val_if_fail(outbox
!= NULL
, -1);
1170 outbox_path
= folder_item_get_path(outbox
);
1171 debug_print("attempting to save sent message to %s...\n", outbox_path
);
1172 g_free(outbox_path
);
1174 gchar tmp
[MAXPATHLEN
+ 1];
1176 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg.out.%08x",
1177 get_rc_dir(), G_DIR_SEPARATOR
, (guint
) rand());
1179 if (procmsg_remove_special_headers(file
, tmp
) !=0)
1182 while (folder_item_scan(outbox
) < 0) {
1183 outbox
= foldersel_folder_sel(NULL
, FOLDER_SEL_SAVE
, NULL
, FALSE
,
1184 _("Select the folder where you want to save the sent message"));
1185 if (outbox
== NULL
) {
1186 g_warning("not saving sent message");
1191 if ((num
= folder_item_add_msg(outbox
, tmp
, &flag
, TRUE
)) < 0) {
1192 g_warning("not saving sent message");
1193 outbox_path
= folder_item_get_path(outbox
);
1194 alertpanel_warning(_("Could not save sent message to %s."), outbox_path
);
1196 g_free(outbox_path
);
1200 msginfo
= folder_item_get_msginfo(outbox
, num
); /* refcnt++ */
1201 tmp_msginfo
= procmsg_msginfo_get_full_info(msginfo
); /* refcnt++ */
1202 if (msginfo
!= NULL
) {
1203 procmsg_msginfo_unset_flags(msginfo
, ~0, 0);
1204 procmsg_msginfo_free(&msginfo
); /* refcnt-- */
1205 /* tmp_msginfo == msginfo */
1206 if (tmp_msginfo
&& msginfo
->extradata
&&
1207 (msginfo
->extradata
->dispositionnotificationto
||
1208 msginfo
->extradata
->returnreceiptto
)) {
1209 procmsg_msginfo_set_flags(msginfo
, MSG_RETRCPT_SENT
, 0);
1211 procmsg_msginfo_free(&tmp_msginfo
); /* refcnt-- */
1218 MsgInfo
*procmsg_msginfo_new_ref(MsgInfo
*msginfo
)
1225 MsgInfo
*procmsg_msginfo_new(void)
1227 MsgInfo
*newmsginfo
;
1229 newmsginfo
= g_new0(MsgInfo
, 1);
1230 newmsginfo
->refcnt
= 1;
1235 static MsgInfoAvatar
*procmsg_msginfoavatar_copy(MsgInfoAvatar
*avatar
)
1237 MsgInfoAvatar
*newavatar
;
1239 if (avatar
== NULL
) return NULL
;
1241 newavatar
= g_new0(MsgInfoAvatar
, 1);
1242 newavatar
->avatar_id
= avatar
->avatar_id
;
1243 newavatar
->avatar_src
= g_strdup(avatar
->avatar_src
);
1248 static void procmsg_msginfoavatar_free(MsgInfoAvatar
*avatar
)
1250 if (avatar
!= NULL
) {
1251 if (avatar
->avatar_src
!= NULL
)
1252 g_free(avatar
->avatar_src
);
1257 MsgInfo
*procmsg_msginfo_copy(MsgInfo
*msginfo
)
1259 MsgInfo
*newmsginfo
;
1262 if (msginfo
== NULL
) return NULL
;
1264 newmsginfo
= g_new0(MsgInfo
, 1);
1266 newmsginfo
->refcnt
= 1;
1268 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1269 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1270 g_strdup(msginfo->mmb) : NULL
1285 MEMBDUP(newsgroups
);
1292 MEMBCOPY(to_folder
);
1294 if (msginfo
->extradata
) {
1295 newmsginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1296 if (msginfo
->extradata
->avatars
) {
1297 newmsginfo
->extradata
->avatars
= g_slist_copy_deep(msginfo
->extradata
->avatars
,
1298 (GCopyFunc
) procmsg_msginfoavatar_copy
, NULL
);
1300 MEMBDUP(extradata
->dispositionnotificationto
);
1301 MEMBDUP(extradata
->returnreceiptto
);
1302 MEMBDUP(extradata
->partial_recv
);
1303 MEMBDUP(extradata
->account_server
);
1304 MEMBDUP(extradata
->account_login
);
1305 MEMBDUP(extradata
->list_post
);
1306 MEMBDUP(extradata
->list_subscribe
);
1307 MEMBDUP(extradata
->list_unsubscribe
);
1308 MEMBDUP(extradata
->list_help
);
1309 MEMBDUP(extradata
->list_archive
);
1310 MEMBDUP(extradata
->list_owner
);
1311 MEMBDUP(extradata
->resent_from
);
1314 refs
= msginfo
->references
;
1315 for (refs
= msginfo
->references
; refs
!= NULL
; refs
= refs
->next
) {
1316 newmsginfo
->references
= g_slist_prepend
1317 (newmsginfo
->references
, g_strdup(refs
->data
));
1319 newmsginfo
->references
= g_slist_reverse(newmsginfo
->references
);
1322 MEMBDUP(plaintext_file
);
1327 MsgInfo
*procmsg_msginfo_get_full_info_from_file(MsgInfo
*msginfo
, const gchar
*file
)
1329 MsgInfo
*full_msginfo
;
1331 if (msginfo
== NULL
) return NULL
;
1333 if (!file
|| !is_file_exist(file
)) {
1334 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file");
1338 full_msginfo
= procheader_parse_file(file
, msginfo
->flags
, TRUE
, FALSE
);
1339 if (!full_msginfo
) return NULL
;
1341 msginfo
->total_size
= full_msginfo
->total_size
;
1342 msginfo
->planned_download
= full_msginfo
->planned_download
;
1344 if (full_msginfo
->extradata
) {
1345 if (!msginfo
->extradata
)
1346 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1347 if (!msginfo
->extradata
->list_post
)
1348 msginfo
->extradata
->list_post
= g_strdup(full_msginfo
->extradata
->list_post
);
1349 if (!msginfo
->extradata
->list_subscribe
)
1350 msginfo
->extradata
->list_subscribe
= g_strdup(full_msginfo
->extradata
->list_subscribe
);
1351 if (!msginfo
->extradata
->list_unsubscribe
)
1352 msginfo
->extradata
->list_unsubscribe
= g_strdup(full_msginfo
->extradata
->list_unsubscribe
);
1353 if (!msginfo
->extradata
->list_help
)
1354 msginfo
->extradata
->list_help
= g_strdup(full_msginfo
->extradata
->list_help
);
1355 if (!msginfo
->extradata
->list_archive
)
1356 msginfo
->extradata
->list_archive
= g_strdup(full_msginfo
->extradata
->list_archive
);
1357 if (!msginfo
->extradata
->list_owner
)
1358 msginfo
->extradata
->list_owner
= g_strdup(full_msginfo
->extradata
->list_owner
);
1359 if (!msginfo
->extradata
->avatars
)
1360 msginfo
->extradata
->avatars
= g_slist_copy_deep(full_msginfo
->extradata
->avatars
,
1361 (GCopyFunc
) procmsg_msginfoavatar_copy
, NULL
);
1362 if (!msginfo
->extradata
->dispositionnotificationto
)
1363 msginfo
->extradata
->dispositionnotificationto
=
1364 g_strdup(full_msginfo
->extradata
->dispositionnotificationto
);
1365 if (!msginfo
->extradata
->returnreceiptto
)
1366 msginfo
->extradata
->returnreceiptto
= g_strdup
1367 (full_msginfo
->extradata
->returnreceiptto
);
1368 if (!msginfo
->extradata
->partial_recv
&& full_msginfo
->extradata
->partial_recv
)
1369 msginfo
->extradata
->partial_recv
= g_strdup
1370 (full_msginfo
->extradata
->partial_recv
);
1371 if (!msginfo
->extradata
->account_server
&& full_msginfo
->extradata
->account_server
)
1372 msginfo
->extradata
->account_server
= g_strdup
1373 (full_msginfo
->extradata
->account_server
);
1374 if (!msginfo
->extradata
->account_login
&& full_msginfo
->extradata
->account_login
)
1375 msginfo
->extradata
->account_login
= g_strdup
1376 (full_msginfo
->extradata
->account_login
);
1377 if (!msginfo
->extradata
->resent_from
&& full_msginfo
->extradata
->resent_from
)
1378 msginfo
->extradata
->resent_from
= g_strdup
1379 (full_msginfo
->extradata
->resent_from
);
1381 procmsg_msginfo_free(&full_msginfo
);
1383 return procmsg_msginfo_new_ref(msginfo
);
1386 MsgInfo
*procmsg_msginfo_get_full_info(MsgInfo
*msginfo
)
1388 MsgInfo
*full_msginfo
;
1391 if (msginfo
== NULL
) return NULL
;
1393 file
= procmsg_get_message_file_path(msginfo
);
1394 if (!file
|| !is_file_exist(file
)) {
1396 file
= procmsg_get_message_file(msginfo
);
1398 if (!file
|| !is_file_exist(file
)) {
1399 g_warning("procmsg_msginfo_get_full_info(): can't get message file");
1403 full_msginfo
= procmsg_msginfo_get_full_info_from_file(msginfo
, file
);
1405 return full_msginfo
;
1408 #define FREENULL(n) { g_free(n); n = NULL; }
1409 void procmsg_msginfo_free(MsgInfo
**msginfo_ptr
)
1411 MsgInfo
*msginfo
= *msginfo_ptr
;
1413 if (msginfo
== NULL
) return;
1416 if (msginfo
->refcnt
> 0)
1419 if (msginfo
->to_folder
) {
1420 msginfo
->to_folder
->op_count
--;
1421 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
1424 FREENULL(msginfo
->fromspace
);
1426 FREENULL(msginfo
->fromname
);
1428 FREENULL(msginfo
->date
);
1429 FREENULL(msginfo
->from
);
1430 FREENULL(msginfo
->to
);
1431 FREENULL(msginfo
->cc
);
1432 FREENULL(msginfo
->newsgroups
);
1433 FREENULL(msginfo
->subject
);
1434 FREENULL(msginfo
->msgid
);
1435 FREENULL(msginfo
->inreplyto
);
1436 FREENULL(msginfo
->xref
);
1438 if (msginfo
->extradata
) {
1439 if (msginfo
->extradata
->avatars
) {
1440 g_slist_foreach(msginfo
->extradata
->avatars
,
1441 (GFunc
)procmsg_msginfoavatar_free
,
1443 g_slist_free(msginfo
->extradata
->avatars
);
1444 msginfo
->extradata
->avatars
= NULL
;
1446 FREENULL(msginfo
->extradata
->returnreceiptto
);
1447 FREENULL(msginfo
->extradata
->dispositionnotificationto
);
1448 FREENULL(msginfo
->extradata
->list_post
);
1449 FREENULL(msginfo
->extradata
->list_subscribe
);
1450 FREENULL(msginfo
->extradata
->list_unsubscribe
);
1451 FREENULL(msginfo
->extradata
->list_help
);
1452 FREENULL(msginfo
->extradata
->list_archive
);
1453 FREENULL(msginfo
->extradata
->list_owner
);
1454 FREENULL(msginfo
->extradata
->partial_recv
);
1455 FREENULL(msginfo
->extradata
->account_server
);
1456 FREENULL(msginfo
->extradata
->account_login
);
1457 FREENULL(msginfo
->extradata
->resent_from
);
1458 FREENULL(msginfo
->extradata
);
1460 slist_free_strings_full(msginfo
->references
);
1461 msginfo
->references
= NULL
;
1462 g_slist_free(msginfo
->tags
);
1463 msginfo
->tags
= NULL
;
1465 FREENULL(msginfo
->plaintext_file
);
1468 *msginfo_ptr
= NULL
;
1472 guint
procmsg_msginfo_memusage(MsgInfo
*msginfo
)
1477 memusage
+= sizeof(MsgInfo
);
1478 if (msginfo
->fromname
)
1479 memusage
+= strlen(msginfo
->fromname
);
1481 memusage
+= strlen(msginfo
->date
);
1483 memusage
+= strlen(msginfo
->from
);
1485 memusage
+= strlen(msginfo
->to
);
1487 memusage
+= strlen(msginfo
->cc
);
1488 if (msginfo
->newsgroups
)
1489 memusage
+= strlen(msginfo
->newsgroups
);
1490 if (msginfo
->subject
)
1491 memusage
+= strlen(msginfo
->subject
);
1493 memusage
+= strlen(msginfo
->msgid
);
1494 if (msginfo
->inreplyto
)
1495 memusage
+= strlen(msginfo
->inreplyto
);
1497 for (tmp
= msginfo
->references
; tmp
; tmp
=tmp
->next
) {
1498 gchar
*r
= (gchar
*)tmp
->data
;
1499 memusage
+= r
?strlen(r
):0 + sizeof(GSList
);
1501 if (msginfo
->fromspace
)
1502 memusage
+= strlen(msginfo
->fromspace
);
1504 for (tmp
= msginfo
->tags
; tmp
; tmp
=tmp
->next
) {
1505 memusage
+= sizeof(GSList
);
1507 if (msginfo
->extradata
) {
1508 memusage
+= sizeof(MsgInfoExtraData
);
1509 if (msginfo
->extradata
->avatars
) {
1510 for (tmp
= msginfo
->extradata
->avatars
; tmp
; tmp
= tmp
->next
) {
1511 MsgInfoAvatar
*avt
= (MsgInfoAvatar
*)tmp
->data
;
1512 memusage
+= (avt
->avatar_src
)? strlen(avt
->avatar_src
): 0;
1513 memusage
+= sizeof(MsgInfoAvatar
) + sizeof(GSList
);
1516 if (msginfo
->extradata
->dispositionnotificationto
)
1517 memusage
+= strlen(msginfo
->extradata
->dispositionnotificationto
);
1518 if (msginfo
->extradata
->returnreceiptto
)
1519 memusage
+= strlen(msginfo
->extradata
->returnreceiptto
);
1521 if (msginfo
->extradata
->partial_recv
)
1522 memusage
+= strlen(msginfo
->extradata
->partial_recv
);
1523 if (msginfo
->extradata
->account_server
)
1524 memusage
+= strlen(msginfo
->extradata
->account_server
);
1525 if (msginfo
->extradata
->account_login
)
1526 memusage
+= strlen(msginfo
->extradata
->account_login
);
1527 if (msginfo
->extradata
->resent_from
)
1528 memusage
+= strlen(msginfo
->extradata
->resent_from
);
1530 if (msginfo
->extradata
->list_post
)
1531 memusage
+= strlen(msginfo
->extradata
->list_post
);
1532 if (msginfo
->extradata
->list_subscribe
)
1533 memusage
+= strlen(msginfo
->extradata
->list_subscribe
);
1534 if (msginfo
->extradata
->list_unsubscribe
)
1535 memusage
+= strlen(msginfo
->extradata
->list_unsubscribe
);
1536 if (msginfo
->extradata
->list_help
)
1537 memusage
+= strlen(msginfo
->extradata
->list_help
);
1538 if (msginfo
->extradata
->list_archive
)
1539 memusage
+= strlen(msginfo
->extradata
->list_archive
);
1540 if (msginfo
->extradata
->list_owner
)
1541 memusage
+= strlen(msginfo
->extradata
->list_owner
);
1546 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
1547 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1549 static HeaderEntry qentry
[] = {
1550 {"S:", NULL
, FALSE
}, /* 0 */
1551 {"SSV:", NULL
, FALSE
},
1552 {"R:", NULL
, FALSE
},
1553 {"NG:", NULL
, FALSE
},
1554 {"MAID:", NULL
, FALSE
},
1555 {"NAID:", NULL
, FALSE
}, /* 5 */
1556 {"SCF:", NULL
, FALSE
},
1557 {"RMID:", NULL
, FALSE
},
1558 {"FMID:", NULL
, FALSE
},
1559 {"X-Claws-Privacy-System:", NULL
, FALSE
},
1560 {"X-Claws-Encrypt:", NULL
, FALSE
}, /* 10 */
1561 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
1562 {"X-Claws-End-Special-Headers:", NULL
, FALSE
},
1563 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
1564 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
1565 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
}, /* 15 */
1566 {"X-Sylpheed-End-Special-Headers:", NULL
, FALSE
},
1567 {NULL
, NULL
, FALSE
}};
1570 gint mailval
= 0, newsval
= 0;
1572 gchar
*smtpserver
= NULL
;
1573 GSList
*to_list
= NULL
;
1574 GSList
*newsgroup_list
= NULL
;
1575 gchar
*savecopyfolder
= NULL
;
1576 gchar
*replymessageid
= NULL
;
1577 gchar
*fwdmessageid
= NULL
;
1580 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
1581 gboolean encrypt
= FALSE
;
1584 cm_return_val_if_fail(file
!= NULL
, -1);
1586 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
1587 FILE_OP_ERROR(file
, "claws_fopen");
1589 if (*errstr
) g_free(*errstr
);
1590 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1595 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
1596 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
1604 if (smtpserver
== NULL
)
1605 smtpserver
= g_strdup(p
);
1608 to_list
= address_list_append(to_list
, p
);
1611 newsgroup_list
= newsgroup_list_append(newsgroup_list
, p
);
1613 case Q_MAIL_ACCOUNT_ID
:
1614 mailac
= account_find_from_id(atoi(p
));
1616 case Q_NEWS_ACCOUNT_ID
:
1617 newsac
= account_find_from_id(atoi(p
));
1619 case Q_SAVE_COPY_FOLDER
:
1620 if (savecopyfolder
== NULL
)
1621 savecopyfolder
= g_strdup(p
);
1623 case Q_REPLY_MESSAGE_ID
:
1624 if (replymessageid
== NULL
)
1625 replymessageid
= g_strdup(p
);
1627 case Q_FWD_MESSAGE_ID
:
1628 if (fwdmessageid
== NULL
)
1629 fwdmessageid
= g_strdup(p
);
1637 case Q_CLAWS_HDRS_OLD
:
1638 /* end of special headers reached */
1640 goto send_mail
; /* can't "break;break;" */
1646 filepos
= ftell(fp
);
1648 FILE_OP_ERROR(file
, "ftell");
1650 if (*errstr
) g_free(*errstr
);
1651 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1657 debug_print("Sending message by mail\n");
1660 if (*errstr
) g_free(*errstr
);
1661 *errstr
= g_strdup_printf(_("Queued message header is broken."));
1664 } else if (mailac
&& mailac
->use_mail_command
&&
1665 mailac
->mail_command
&& (* mailac
->mail_command
)) {
1666 mailval
= send_message_local(mailac
->mail_command
, fp
);
1669 mailac
= account_find_from_smtp_server(from
, smtpserver
);
1671 g_warning("account not found, "
1672 "using current account...");
1673 mailac
= cur_account
;
1678 mailval
= send_message_smtp_full(mailac
, to_list
, fp
, keep_session
);
1679 if (mailval
== -1 && errstr
) {
1680 if (*errstr
) g_free(*errstr
);
1681 *errstr
= g_strdup_printf(_("An error happened during SMTP session."));
1684 PrefsAccount tmp_ac
;
1686 g_warning("account not found");
1688 memset(&tmp_ac
, 0, sizeof(PrefsAccount
));
1689 tmp_ac
.address
= from
;
1690 tmp_ac
.smtp_server
= smtpserver
;
1691 tmp_ac
.smtpport
= SMTP_PORT
;
1692 mailval
= send_message_smtp(&tmp_ac
, to_list
, fp
);
1693 if (mailval
== -1 && errstr
) {
1694 if (*errstr
) g_free(*errstr
);
1695 *errstr
= g_strdup_printf(_("No specific account has been found to "
1696 "send, and an error happened during SMTP session."));
1700 } else if (!to_list
&& !newsgroup_list
) {
1702 if (*errstr
) g_free(*errstr
);
1703 *errstr
= g_strdup(_("Couldn't determine sending information. "
1704 "Maybe the email hasn't been generated by Claws Mail."));
1709 if (fseek(fp
, filepos
, SEEK_SET
) < 0) {
1710 FILE_OP_ERROR(file
, "fseek");
1714 if (newsgroup_list
&& newsac
&& (mailval
== 0)) {
1717 gchar buf
[BUFFSIZE
];
1720 /* write to temporary file */
1721 tmp
= g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1722 G_DIR_SEPARATOR
, file
);
1723 if ((tmpfp
= claws_fopen(tmp
, "wb")) == NULL
) {
1724 FILE_OP_ERROR(tmp
, "claws_fopen");
1726 alertpanel_error(_("Couldn't create temporary file for news sending."));
1728 if (change_file_mode_rw(tmpfp
, tmp
) < 0) {
1729 FILE_OP_ERROR(tmp
, "chmod");
1730 g_warning("can't change file mode");
1733 while ((newsval
== 0) && claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1734 if (claws_fputs(buf
, tmpfp
) == EOF
) {
1735 FILE_OP_ERROR(tmp
, "claws_fputs");
1738 if (*errstr
) g_free(*errstr
);
1739 *errstr
= g_strdup_printf(_("Error when writing temporary file for news sending."));
1743 claws_safe_fclose(tmpfp
);
1746 debug_print("Sending message by news\n");
1748 folder
= FOLDER(newsac
->folder
);
1750 newsval
= news_post(folder
, tmp
);
1751 if (newsval
< 0 && errstr
) {
1752 if (*errstr
) g_free(*errstr
);
1753 *errstr
= g_strdup_printf(_("Error occurred while posting the message to %s."),
1754 newsac
->nntp_server
);
1764 /* update session statistics */
1765 if (mailval
== 0 && newsval
== 0) {
1766 /* update session stats */
1768 session_stats
.replied
++;
1769 else if (fwdmessageid
)
1770 session_stats
.forwarded
++;
1772 session_stats
.sent
++;
1775 /* save message to outbox */
1776 if (mailval
== 0 && newsval
== 0 && savecopyfolder
) {
1777 debug_print("saving sent message to %s...\n", savecopyfolder
);
1779 if (!encrypt
|| !mailac
->save_encrypted_as_clear_text
) {
1780 outbox
= folder_find_item_from_identifier(savecopyfolder
);
1783 outbox
= folder_get_default_outbox();
1784 if (outbox
!= NULL
) {
1785 id
= folder_item_get_identifier(outbox
);
1786 debug_print("%s not found, using %s\n", savecopyfolder
, id
);
1789 debug_print("could not find outbox\n");
1792 /* Mail was not saved to outbox before encrypting, save it now. */
1793 gboolean saved
= FALSE
;
1794 *queued_removed
= FALSE
;
1795 if (queue
&& msgnum
> 0) {
1796 MsgInfo
*queued_mail
= folder_item_get_msginfo(queue
, msgnum
);
1797 if (folder_item_move_msg(outbox
, queued_mail
) >= 0) {
1798 debug_print("moved queued mail %d to sent folder\n", msgnum
);
1800 *queued_removed
= TRUE
;
1801 } else if (folder_item_copy_msg(outbox
, queued_mail
) >= 0) {
1802 debug_print("copied queued mail %d to sent folder\n", msgnum
);
1805 procmsg_msginfo_free(&queued_mail
);
1808 debug_print("resaving queued mail to sent folder\n");
1809 procmsg_save_to_outbox(outbox
, file
);
1814 if (replymessageid
!= NULL
|| fwdmessageid
!= NULL
) {
1818 if (replymessageid
!= NULL
)
1819 tokens
= g_strsplit(replymessageid
, "\t", 0);
1821 tokens
= g_strsplit(fwdmessageid
, "\t", 0);
1822 item
= folder_find_item_from_identifier(tokens
[0]);
1824 /* check if queued message has valid folder and message id */
1825 if (item
!= NULL
&& tokens
[2] != NULL
) {
1828 msginfo
= folder_item_get_msginfo(item
, atoi(tokens
[1]));
1830 /* check if referring message exists and has a message id */
1831 if ((msginfo
!= NULL
) &&
1832 (msginfo
->msgid
!= NULL
) &&
1833 (strcmp(msginfo
->msgid
, tokens
[2]) != 0)) {
1834 procmsg_msginfo_free(&msginfo
);
1838 if (msginfo
== NULL
) {
1839 msginfo
= folder_item_get_msginfo_by_msgid(item
, tokens
[2]);
1842 if (msginfo
!= NULL
) {
1843 if (replymessageid
!= NULL
) {
1844 MsgPermFlags to_unset
= 0;
1846 if (prefs_common
.mark_as_read_on_new_window
)
1847 to_unset
= (MSG_NEW
|MSG_UNREAD
);
1849 procmsg_msginfo_unset_flags(msginfo
, to_unset
, 0);
1850 procmsg_msginfo_set_flags(msginfo
, MSG_REPLIED
, 0);
1852 procmsg_msginfo_set_flags(msginfo
, MSG_FORWARDED
, 0);
1854 procmsg_msginfo_free(&msginfo
);
1862 slist_free_strings_full(to_list
);
1863 slist_free_strings_full(newsgroup_list
);
1864 g_free(savecopyfolder
);
1865 g_free(replymessageid
);
1866 g_free(fwdmessageid
);
1868 return (newsval
!= 0 ? newsval
: mailval
);
1871 gint
procmsg_send_message_queue(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1873 gint result
= procmsg_send_message_queue_full(file
, FALSE
, errstr
, queue
, msgnum
, queued_removed
);
1874 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1875 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1879 gint
procmsg_send_message_queue_with_lock(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1882 if (procmsg_queue_lock(errstr
)) {
1883 val
= procmsg_send_message_queue(file
, errstr
, queue
, msgnum
, queued_removed
);
1884 procmsg_queue_unlock();
1890 static void update_folder_msg_counts(FolderItem
*item
, MsgInfo
*msginfo
, MsgPermFlags old_flags
)
1892 MsgPermFlags new_flags
= msginfo
->flags
.perm_flags
;
1895 if (!(old_flags
& MSG_NEW
) && (new_flags
& MSG_NEW
)) {
1899 if ((old_flags
& MSG_NEW
) && !(new_flags
& MSG_NEW
)) {
1904 if (!(old_flags
& MSG_UNREAD
) && (new_flags
& MSG_UNREAD
)) {
1905 item
->unread_msgs
++;
1906 if (procmsg_msg_has_marked_parent(msginfo
))
1907 item
->unreadmarked_msgs
++;
1910 if ((old_flags
& MSG_UNREAD
) && !(new_flags
& MSG_UNREAD
)) {
1911 item
->unread_msgs
--;
1912 if (procmsg_msg_has_marked_parent(msginfo
))
1913 item
->unreadmarked_msgs
--;
1917 if (!(old_flags
& MSG_MARKED
) && (new_flags
& MSG_MARKED
)) {
1918 procmsg_update_unread_children(msginfo
, TRUE
);
1919 item
->marked_msgs
++;
1922 if ((old_flags
& MSG_MARKED
) && !(new_flags
& MSG_MARKED
)) {
1923 procmsg_update_unread_children(msginfo
, FALSE
);
1924 item
->marked_msgs
--;
1927 if (!(old_flags
& MSG_REPLIED
) && (new_flags
& MSG_REPLIED
)) {
1928 item
->replied_msgs
++;
1931 if ((old_flags
& MSG_REPLIED
) && !(new_flags
& MSG_REPLIED
)) {
1932 item
->replied_msgs
--;
1935 if (!(old_flags
& MSG_FORWARDED
) && (new_flags
& MSG_FORWARDED
)) {
1936 item
->forwarded_msgs
++;
1939 if ((old_flags
& MSG_FORWARDED
) && !(new_flags
& MSG_FORWARDED
)) {
1940 item
->forwarded_msgs
--;
1943 if (!(old_flags
& MSG_LOCKED
) && (new_flags
& MSG_LOCKED
)) {
1944 item
->locked_msgs
++;
1947 if ((old_flags
& MSG_LOCKED
) && !(new_flags
& MSG_LOCKED
)) {
1948 item
->locked_msgs
--;
1951 if ((old_flags
& MSG_IGNORE_THREAD
) && !(new_flags
& MSG_IGNORE_THREAD
)) {
1952 item
->ignored_msgs
--;
1955 if (!(old_flags
& MSG_IGNORE_THREAD
) && (new_flags
& MSG_IGNORE_THREAD
)) {
1956 item
->ignored_msgs
++;
1959 if ((old_flags
& MSG_WATCH_THREAD
) && !(new_flags
& MSG_WATCH_THREAD
)) {
1960 item
->watched_msgs
--;
1963 if (!(old_flags
& MSG_WATCH_THREAD
) && (new_flags
& MSG_WATCH_THREAD
)) {
1964 item
->watched_msgs
++;
1968 void procmsg_msginfo_set_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
1971 MsgInfoUpdate msginfo_update
;
1972 MsgPermFlags perm_flags_new
, perm_flags_old
;
1973 MsgTmpFlags tmp_flags_old
;
1975 cm_return_if_fail(msginfo
!= NULL
);
1976 item
= msginfo
->folder
;
1977 cm_return_if_fail(item
!= NULL
);
1979 debug_print("Setting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
1981 /* Perm Flags handling */
1982 perm_flags_old
= msginfo
->flags
.perm_flags
;
1983 perm_flags_new
= msginfo
->flags
.perm_flags
| perm_flags
;
1984 if ((perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
1985 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
1987 if ((perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
1988 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
1991 if (perm_flags_old
!= perm_flags_new
) {
1992 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
1994 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
1995 summary_update_unread(mainwindow_get_mainwindow()->summaryview
, NULL
);
1998 /* Tmp flags handling */
1999 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2000 msginfo
->flags
.tmp_flags
|= tmp_flags
;
2002 /* update notification */
2003 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2004 msginfo_update
.msginfo
= msginfo
;
2005 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2006 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2007 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2011 void procmsg_msginfo_unset_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
2014 MsgInfoUpdate msginfo_update
;
2015 MsgPermFlags perm_flags_new
, perm_flags_old
;
2016 MsgTmpFlags tmp_flags_old
;
2018 cm_return_if_fail(msginfo
!= NULL
);
2019 item
= msginfo
->folder
;
2020 cm_return_if_fail(item
!= NULL
);
2022 debug_print("Unsetting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
2024 /* Perm Flags handling */
2025 perm_flags_old
= msginfo
->flags
.perm_flags
;
2026 perm_flags_new
= msginfo
->flags
.perm_flags
& ~perm_flags
;
2028 if (perm_flags_old
!= perm_flags_new
) {
2029 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2031 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2034 /* Tmp flags hanlding */
2035 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2036 msginfo
->flags
.tmp_flags
&= ~tmp_flags
;
2038 /* update notification */
2039 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2040 msginfo_update
.msginfo
= msginfo
;
2041 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2042 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2043 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2047 void procmsg_msginfo_change_flags(MsgInfo
*msginfo
,
2048 MsgPermFlags add_perm_flags
, MsgTmpFlags add_tmp_flags
,
2049 MsgPermFlags rem_perm_flags
, MsgTmpFlags rem_tmp_flags
)
2052 MsgInfoUpdate msginfo_update
;
2053 MsgPermFlags perm_flags_new
, perm_flags_old
;
2054 MsgTmpFlags tmp_flags_old
;
2056 cm_return_if_fail(msginfo
!= NULL
);
2057 item
= msginfo
->folder
;
2058 cm_return_if_fail(item
!= NULL
);
2060 debug_print("Changing flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
2062 /* Perm Flags handling */
2063 perm_flags_old
= msginfo
->flags
.perm_flags
;
2064 perm_flags_new
= (msginfo
->flags
.perm_flags
& ~rem_perm_flags
) | add_perm_flags
;
2065 if ((add_perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
2066 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
2068 if ((add_perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
2069 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
2072 if (perm_flags_old
!= perm_flags_new
) {
2073 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2075 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2079 /* Tmp flags handling */
2080 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2081 msginfo
->flags
.tmp_flags
&= ~rem_tmp_flags
;
2082 msginfo
->flags
.tmp_flags
|= add_tmp_flags
;
2084 /* update notification */
2085 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2086 msginfo_update
.msginfo
= msginfo
;
2087 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2088 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2089 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2094 *\brief check for flags (e.g. mark) in prior msgs of current thread
2096 *\param info Current message
2097 *\param perm_flags Flags to be checked
2098 *\param parentmsgs Hash of prior msgs to avoid loops
2100 *\return gboolean TRUE if perm_flags are found
2102 static gboolean
procmsg_msg_has_flagged_parent_real(MsgInfo
*info
,
2103 MsgPermFlags perm_flags
, GHashTable
*parentmsgs
)
2107 cm_return_val_if_fail(info
!= NULL
, FALSE
);
2109 if (info
!= NULL
&& info
->folder
!= NULL
&& info
->inreplyto
!= NULL
) {
2110 tmp
= folder_item_get_msginfo_by_msgid(info
->folder
,
2112 if (tmp
&& (tmp
->flags
.perm_flags
& perm_flags
)) {
2113 procmsg_msginfo_free(&tmp
);
2115 } else if (tmp
!= NULL
) {
2118 if (g_hash_table_lookup(parentmsgs
, info
)) {
2119 debug_print("loop detected: %d\n",
2123 g_hash_table_insert(parentmsgs
, info
, "1");
2124 result
= procmsg_msg_has_flagged_parent_real(
2125 tmp
, perm_flags
, parentmsgs
);
2127 procmsg_msginfo_free(&tmp
);
2137 *\brief Callback for cleaning up hash of parentmsgs
2139 static gboolean
parentmsgs_hash_remove(gpointer key
,
2147 *\brief Set up list of parentmsgs
2148 * See procmsg_msg_has_flagged_parent_real()
2150 gboolean
procmsg_msg_has_flagged_parent(MsgInfo
*info
, MsgPermFlags perm_flags
)
2153 static GHashTable
*parentmsgs
= NULL
;
2155 if (parentmsgs
== NULL
)
2156 parentmsgs
= g_hash_table_new(NULL
, NULL
);
2158 result
= procmsg_msg_has_flagged_parent_real(info
, perm_flags
, parentmsgs
);
2159 g_hash_table_foreach_remove(parentmsgs
, parentmsgs_hash_remove
, NULL
);
2165 *\brief Check if msgs prior in thread are marked
2166 * See procmsg_msg_has_flagged_parent_real()
2168 gboolean
procmsg_msg_has_marked_parent(MsgInfo
*info
)
2170 return procmsg_msg_has_flagged_parent(info
, MSG_MARKED
);
2174 static GSList
*procmsg_find_children_func(MsgInfo
*info
,
2175 GSList
*children
, GSList
*all
)
2179 cm_return_val_if_fail(info
!=NULL
, children
);
2180 if (info
->msgid
== NULL
)
2183 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2184 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2185 if (tmp
->inreplyto
&& !strcmp(tmp
->inreplyto
, info
->msgid
)) {
2186 /* Check if message is already in the list */
2187 if ((children
== NULL
) ||
2188 (g_slist_index(children
, tmp
) == -1)) {
2189 children
= g_slist_prepend(children
,
2190 procmsg_msginfo_new_ref(tmp
));
2191 children
= procmsg_find_children_func(tmp
,
2200 static GSList
*procmsg_find_children (MsgInfo
*info
)
2205 cm_return_val_if_fail(info
!=NULL
, NULL
);
2206 all
= folder_item_get_msg_list(info
->folder
);
2207 children
= procmsg_find_children_func(info
, NULL
, all
);
2208 if (children
!= NULL
) {
2209 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2210 /* this will not free the used pointers
2211 created with procmsg_msginfo_new_ref */
2212 procmsg_msginfo_free((MsgInfo
**)&(cur
->data
));
2220 static void procmsg_update_unread_children(MsgInfo
*info
, gboolean newly_marked
)
2222 GSList
*children
= procmsg_find_children(info
);
2224 for (cur
= children
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2225 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2226 if(MSG_IS_UNREAD(tmp
->flags
) && !MSG_IS_IGNORE_THREAD(tmp
->flags
)) {
2228 info
->folder
->unreadmarked_msgs
++;
2230 info
->folder
->unreadmarked_msgs
--;
2231 folder_item_update(info
->folder
, F_ITEM_UPDATE_MSGCNT
);
2233 procmsg_msginfo_free(&tmp
);
2235 g_slist_free(children
);
2239 * Set the destination folder for a copy or move operation
2241 * \param msginfo The message which's destination folder is changed
2242 * \param to_folder The destination folder for the operation
2244 void procmsg_msginfo_set_to_folder(MsgInfo
*msginfo
, FolderItem
*to_folder
)
2246 if(msginfo
->to_folder
!= NULL
) {
2247 msginfo
->to_folder
->op_count
--;
2248 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2250 msginfo
->to_folder
= to_folder
;
2251 if(to_folder
!= NULL
) {
2252 to_folder
->op_count
++;
2253 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2258 * Apply filtering actions to the msginfo
2260 * \param msginfo The MsgInfo describing the message that should be filtered
2261 * \return TRUE if the message was moved and MsgInfo is now invalid,
2264 static gboolean
procmsg_msginfo_filter(MsgInfo
*msginfo
, PrefsAccount
* ac_prefs
)
2266 MailFilteringData mail_filtering_data
;
2268 mail_filtering_data
.msginfo
= msginfo
;
2269 mail_filtering_data
.msglist
= NULL
;
2270 mail_filtering_data
.filtered
= NULL
;
2271 mail_filtering_data
.unfiltered
= NULL
;
2272 mail_filtering_data
.account
= ac_prefs
;
2274 if (!ac_prefs
|| ac_prefs
->filterhook_on_recv
)
2275 if (hooks_invoke(MAIL_FILTERING_HOOKLIST
, &mail_filtering_data
))
2278 /* filter if enabled in prefs or move to inbox if not */
2279 if((filtering_rules
!= NULL
) &&
2280 filter_message_by_msginfo(filtering_rules
, msginfo
, ac_prefs
,
2281 FILTERING_INCORPORATION
, NULL
)) {
2288 void procmsg_msglist_filter(GSList
*list
, PrefsAccount
*ac
,
2289 GSList
**filtered
, GSList
**unfiltered
,
2292 GSList
*cur
, *to_do
= NULL
;
2293 gint total
= 0, curnum
= 0;
2294 MailFilteringData mail_filtering_data
;
2296 cm_return_if_fail(filtered
!= NULL
);
2297 cm_return_if_fail(unfiltered
!= NULL
);
2305 total
= g_slist_length(list
);
2309 *unfiltered
= g_slist_copy(list
);
2313 statusbar_print_all(_("Filtering messages...\n"));
2315 mail_filtering_data
.msginfo
= NULL
;
2316 mail_filtering_data
.msglist
= list
;
2317 mail_filtering_data
.filtered
= NULL
;
2318 mail_filtering_data
.unfiltered
= NULL
;
2319 mail_filtering_data
.account
= ac
;
2321 if (!ac
|| ac
->filterhook_on_recv
)
2322 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST
, &mail_filtering_data
);
2324 if (mail_filtering_data
.filtered
== NULL
&&
2325 mail_filtering_data
.unfiltered
== NULL
) {
2326 /* nothing happened */
2327 debug_print(MAIL_LISTFILTERING_HOOKLIST
" did nothing. filtering whole list normally.\n");
2330 if (mail_filtering_data
.filtered
!= NULL
) {
2331 /* keep track of what's been filtered by the hooks */
2332 debug_print(MAIL_LISTFILTERING_HOOKLIST
" filtered some stuff. total %d filtered %d unfilt %d.\n",
2333 g_slist_length(list
),
2334 g_slist_length(mail_filtering_data
.filtered
),
2335 g_slist_length(mail_filtering_data
.unfiltered
));
2337 *filtered
= g_slist_copy(mail_filtering_data
.filtered
);
2339 if (mail_filtering_data
.unfiltered
!= NULL
) {
2340 /* what the hooks didn't handle will go in filtered or
2341 * unfiltered in the next loop */
2342 debug_print(MAIL_LISTFILTERING_HOOKLIST
" left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2343 g_slist_length(list
),
2344 g_slist_length(mail_filtering_data
.filtered
),
2345 g_slist_length(mail_filtering_data
.unfiltered
));
2346 to_do
= mail_filtering_data
.unfiltered
;
2349 for (cur
= to_do
; cur
; cur
= cur
->next
) {
2350 MsgInfo
*info
= (MsgInfo
*)cur
->data
;
2351 if (procmsg_msginfo_filter(info
, ac
))
2352 *filtered
= g_slist_prepend(*filtered
, info
);
2354 *unfiltered
= g_slist_prepend(*unfiltered
, info
);
2355 statusbar_progress_all(curnum
++, total
, prefs_common
.statusbar_update_step
);
2358 g_slist_free(mail_filtering_data
.filtered
);
2359 g_slist_free(mail_filtering_data
.unfiltered
);
2361 *filtered
= g_slist_reverse(*filtered
);
2362 *unfiltered
= g_slist_reverse(*unfiltered
);
2364 statusbar_progress_all(0,0,0);
2365 statusbar_pop_all();
2368 MsgInfo
*procmsg_msginfo_new_from_mimeinfo(MsgInfo
*src_msginfo
, MimeInfo
*mimeinfo
)
2370 MsgInfo
*tmp_msginfo
= NULL
;
2371 MsgFlags flags
= {0, 0};
2372 gchar
*tmpfile
= get_tmp_file();
2373 FILE *fp
= claws_fopen(tmpfile
, "wb");
2375 if (!mimeinfo
|| mimeinfo
->type
!= MIMETYPE_MESSAGE
||
2376 g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
2377 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2384 if (fp
&& procmime_write_mimeinfo(mimeinfo
, fp
) >= 0) {
2385 claws_safe_fclose(fp
);
2387 tmp_msginfo
= procheader_parse_file(
2392 claws_safe_fclose(fp
);
2394 if (tmp_msginfo
!= NULL
) {
2396 tmp_msginfo
->folder
= src_msginfo
->folder
;
2397 tmp_msginfo
->plaintext_file
= g_strdup(tmpfile
);
2399 g_warning("procmsg_msginfo_new_from_mimeinfo(): can't generate new msginfo");
2407 static GSList
*spam_learners
= NULL
;
2409 void procmsg_register_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2411 if (!g_slist_find(spam_learners
, learn_func
))
2412 spam_learners
= g_slist_append(spam_learners
, learn_func
);
2413 if (mainwindow_get_mainwindow()) {
2414 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2415 summary_set_menu_sensitive(
2416 mainwindow_get_mainwindow()->summaryview
);
2417 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2421 void procmsg_unregister_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2423 spam_learners
= g_slist_remove(spam_learners
, learn_func
);
2424 if (mainwindow_get_mainwindow()) {
2425 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2426 summary_set_menu_sensitive(
2427 mainwindow_get_mainwindow()->summaryview
);
2428 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2432 gboolean
procmsg_spam_can_learn(void)
2434 return g_slist_length(spam_learners
) > 0;
2437 int procmsg_spam_learner_learn (MsgInfo
*info
, GSList
*list
, gboolean spam
)
2439 GSList
*cur
= spam_learners
;
2441 for (; cur
; cur
= cur
->next
) {
2442 int ((*func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
)) = cur
->data
;
2443 ret
|= func(info
, list
, spam
);
2448 static gchar
*spam_folder_item
= NULL
;
2449 static FolderItem
* (*procmsg_spam_get_folder_func
)(MsgInfo
*msginfo
) = NULL
;
2450 void procmsg_spam_set_folder (const char *item_identifier
, FolderItem
*(*spam_get_folder_func
)(MsgInfo
*info
))
2452 g_free(spam_folder_item
);
2453 if (item_identifier
)
2454 spam_folder_item
= g_strdup(item_identifier
);
2456 spam_folder_item
= NULL
;
2457 if (spam_get_folder_func
!= NULL
)
2458 procmsg_spam_get_folder_func
= spam_get_folder_func
;
2460 procmsg_spam_get_folder_func
= NULL
;
2463 FolderItem
*procmsg_spam_get_folder (MsgInfo
*msginfo
)
2465 FolderItem
*item
= NULL
;
2467 if (procmsg_spam_get_folder_func
)
2468 item
= procmsg_spam_get_folder_func(msginfo
);
2469 if (item
== NULL
&& spam_folder_item
)
2470 item
= folder_find_item_from_identifier(spam_folder_item
);
2472 item
= folder_get_default_trash();
2476 static void item_has_queued_mails(FolderItem
*item
, gpointer data
)
2478 gboolean
*result
= (gboolean
*)data
;
2479 if (*result
== TRUE
)
2481 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
2482 if (item
->total_msgs
== 0)
2485 GSList
*msglist
= folder_item_get_msg_list(item
);
2487 for (cur
= msglist
; cur
; cur
= cur
->next
) {
2488 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
2489 if (!MSG_IS_DELETED(msginfo
->flags
) &&
2490 !MSG_IS_LOCKED(msginfo
->flags
)) {
2495 procmsg_msg_list_free(msglist
);
2500 gboolean
procmsg_have_queued_mails_fast (void)
2502 gboolean result
= FALSE
;
2503 folder_func_to_all_folders(item_has_queued_mails
, &result
);
2507 static void item_has_trashed_mails(FolderItem
*item
, gpointer data
)
2509 gboolean
*result
= (gboolean
*)data
;
2510 if (*result
== TRUE
)
2512 if (folder_has_parent_of_type(item
, F_TRASH
) && item
->total_msgs
> 0)
2516 gboolean
procmsg_have_trashed_mails_fast (void)
2518 gboolean result
= FALSE
;
2519 folder_func_to_all_folders(item_has_trashed_mails
, &result
);
2523 gchar
*procmsg_msginfo_get_tags_str(MsgInfo
*msginfo
)
2531 if (msginfo
->tags
== NULL
)
2533 for (cur
= msginfo
->tags
; cur
; cur
= cur
->next
) {
2534 const gchar
*tag
= tags_get_tag(GPOINTER_TO_INT(cur
->data
));
2538 tags
= g_strdup(tag
);
2540 int olen
= strlen(tags
);
2541 int nlen
= olen
+ strlen(tag
) + 2 /* strlen(", ") */;
2542 tags
= g_realloc(tags
, nlen
+1);
2545 strcpy(tags
+olen
, ", ");
2546 strcpy(tags
+olen
+2, tag
);
2553 void procmsg_msginfo_update_tags(MsgInfo
*msginfo
, gboolean set
, gint id
)
2561 msginfo
->tags
= g_slist_remove(
2563 GINT_TO_POINTER(id
));
2564 changed
.data
= GINT_TO_POINTER(id
);
2565 changed
.next
= NULL
;
2566 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, &changed
);
2568 if (!g_slist_find(msginfo
->tags
, GINT_TO_POINTER(id
))) {
2569 msginfo
->tags
= g_slist_append(
2571 GINT_TO_POINTER(id
));
2573 changed
.data
= GINT_TO_POINTER(id
);
2574 changed
.next
= NULL
;
2575 folder_item_commit_tags(msginfo
->folder
, msginfo
, &changed
, NULL
);
2580 void procmsg_msginfo_clear_tags(MsgInfo
*msginfo
)
2582 GSList
*unset
= msginfo
->tags
;
2583 msginfo
->tags
= NULL
;
2584 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, unset
);
2585 g_slist_free(unset
);