2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 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/>.
22 #include <glib/gi18n.h>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
52 #include "file-utils.h"
54 extern SessionStats session_stats
;
56 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
57 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
);
58 static void procmsg_update_unread_children (MsgInfo
*info
,
59 gboolean newly_marked
);
66 Q_MAIL_ACCOUNT_ID
= 4,
67 Q_NEWS_ACCOUNT_ID
= 5,
68 Q_SAVE_COPY_FOLDER
= 6,
69 Q_REPLY_MESSAGE_ID
= 7,
75 Q_PRIVACY_SYSTEM_OLD
= 13,
77 Q_ENCRYPT_DATA_OLD
= 15,
78 Q_CLAWS_HDRS_OLD
= 16,
81 void procmsg_msg_list_free(GSList
*mlist
)
86 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
87 msginfo
= (MsgInfo
*)cur
->data
;
88 procmsg_msginfo_free(&msginfo
);
93 MsgNumberList
*procmsg_get_number_list_for_msgs(MsgInfoList
*msglist
)
98 for (cur
= msglist
; cur
; cur
= cur
->next
) {
99 MsgInfo
*msg
= (MsgInfo
*)cur
->data
;
100 nums
= g_slist_prepend(nums
, GUINT_TO_POINTER(msg
->msgnum
));
103 return g_slist_reverse(nums
);
115 /* CLAWS subject threading:
117 in the first round it inserts subject lines in a
118 hashtable (subject <-> node)
120 the second round finishes the threads by attaching
121 matching subject lines to the one found in the
122 hashtable. will use the oldest node with the same
123 subject that is not more then thread_by_subject_max_age
124 days old (see subject_hashtable_lookup)
127 static void subject_hashtable_insert(GHashTable
*hashtable
, GNode
*node
)
133 cm_return_if_fail(hashtable
!= NULL
);
134 cm_return_if_fail(node
!= NULL
);
135 msginfo
= (MsgInfo
*) node
->data
;
136 cm_return_if_fail(msginfo
!= NULL
);
138 subject
= msginfo
->subject
;
142 subject
+= subject_get_prefix_length(subject
);
144 list
= g_hash_table_lookup(hashtable
, subject
);
145 list
= g_slist_prepend(list
, node
);
146 g_hash_table_insert(hashtable
, subject
, list
);
149 static GNode
*subject_hashtable_lookup(GHashTable
*hashtable
, MsgInfo
*msginfo
)
153 GNode
*node
= NULL
, *hashtable_node
= NULL
;
155 MsgInfo
*hashtable_msginfo
= NULL
, *best_msginfo
= NULL
;
158 cm_return_val_if_fail(hashtable
!= NULL
, NULL
);
160 subject
= msginfo
->subject
;
163 prefix_length
= subject_get_prefix_length(subject
);
164 if (prefix_length
<= 0)
166 subject
+= prefix_length
;
168 list
= g_hash_table_lookup(hashtable
, subject
);
172 /* check all nodes with the same subject to find the best parent */
173 for (cur
= list
; cur
; cur
= cur
->next
) {
174 hashtable_node
= (GNode
*)cur
->data
;
175 hashtable_msginfo
= (MsgInfo
*) hashtable_node
->data
;
178 /* best node should be the oldest in the found nodes */
179 /* parent node must not be older then msginfo */
180 if ((hashtable_msginfo
->date_t
< msginfo
->date_t
) &&
181 ((best_msginfo
== NULL
) ||
182 (best_msginfo
->date_t
> hashtable_msginfo
->date_t
)))
185 /* parent node must not be more then thread_by_subject_max_age
186 days older then msginfo */
187 if (fabs(difftime(msginfo
->date_t
, hashtable_msginfo
->date_t
)) >
188 prefs_common
.thread_by_subject_max_age
* 3600 * 24)
191 /* can add new tests for all matching
192 nodes found by subject */
195 node
= hashtable_node
;
196 best_msginfo
= hashtable_msginfo
;
203 static void subject_hashtable_free(gpointer key
, gpointer value
, gpointer data
)
208 /* return the reversed thread tree */
209 GNode
*procmsg_get_thread_tree(GSList
*mlist
)
211 GNode
*root
, *parent
, *node
, *next
;
212 GHashTable
*msgid_table
;
213 GHashTable
*subject_hashtable
= NULL
;
218 root
= g_node_new(NULL
);
219 msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
221 if (prefs_common
.thread_by_subject
) {
222 subject_hashtable
= g_hash_table_new(g_str_hash
, g_str_equal
);
225 for (; mlist
!= NULL
; mlist
= mlist
->next
) {
226 msginfo
= (MsgInfo
*)mlist
->data
;
229 if (msginfo
->inreplyto
) {
230 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
231 if (parent
== NULL
) {
235 node
= g_node_insert_data_before
236 (parent
, parent
== root
? parent
->children
: NULL
,
238 if ((msgid
= msginfo
->msgid
) && g_hash_table_lookup(msgid_table
, msgid
) == NULL
)
239 g_hash_table_insert(msgid_table
, (gchar
*)msgid
, node
);
241 /* CLAWS: add subject to hashtable (without prefix) */
242 if (prefs_common
.thread_by_subject
) {
243 subject_hashtable_insert(subject_hashtable
, node
);
247 /* complete the unfinished threads */
248 for (node
= root
->children
; node
!= NULL
; ) {
250 msginfo
= (MsgInfo
*)node
->data
;
253 if (msginfo
->inreplyto
)
254 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
256 /* try looking for the indirect parent */
257 if (!parent
&& msginfo
->references
) {
258 for (reflist
= msginfo
->references
;
259 reflist
!= NULL
; reflist
= reflist
->next
)
260 if ((parent
= g_hash_table_lookup
261 (msgid_table
, reflist
->data
)) != NULL
)
265 /* node should not be the parent, and node should not
266 be an ancestor of parent (circular reference) */
267 if (parent
&& parent
!= node
&&
268 !g_node_is_ancestor(node
, parent
)) {
271 (parent
, parent
->children
, node
);
277 if (prefs_common
.thread_by_subject
) {
278 START_TIMING("thread by subject");
279 for (node
= root
->children
; node
&& node
!= NULL
;) {
281 msginfo
= (MsgInfo
*) node
->data
;
283 parent
= subject_hashtable_lookup(subject_hashtable
, msginfo
);
285 /* the node may already be threaded by IN-REPLY-TO, so go up
287 find the parent node */
288 if (parent
!= NULL
) {
289 if (g_node_is_ancestor(node
, parent
))
297 g_node_append(parent
, node
);
305 if (prefs_common
.thread_by_subject
)
307 g_hash_table_foreach(subject_hashtable
, subject_hashtable_free
, NULL
);
308 g_hash_table_destroy(subject_hashtable
);
311 g_hash_table_destroy(msgid_table
);
316 gint
procmsg_move_messages(GSList
*mlist
)
318 GSList
*cur
, *movelist
= NULL
;
320 FolderItem
*dest
= NULL
;
322 gboolean finished
= TRUE
;
323 if (!mlist
) return 0;
325 folder_item_update_freeze();
328 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
329 msginfo
= (MsgInfo
*)cur
->data
;
330 if (!msginfo
->to_folder
) {
336 dest
= msginfo
->to_folder
;
337 movelist
= g_slist_prepend(movelist
, msginfo
);
338 } else if (dest
== msginfo
->to_folder
) {
339 movelist
= g_slist_prepend(movelist
, msginfo
);
343 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
346 movelist
= g_slist_reverse(movelist
);
347 retval
|= folder_item_move_msgs(dest
, movelist
);
348 g_slist_free(movelist
);
351 if (finished
== FALSE
) {
357 folder_item_update_thaw();
361 void procmsg_copy_messages(GSList
*mlist
)
363 GSList
*cur
, *copylist
= NULL
;
365 FolderItem
*dest
= NULL
;
366 gboolean finished
= TRUE
;
369 folder_item_update_freeze();
372 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
373 msginfo
= (MsgInfo
*)cur
->data
;
374 if (!msginfo
->to_folder
) {
380 dest
= msginfo
->to_folder
;
381 copylist
= g_slist_prepend(copylist
, msginfo
);
382 } else if (dest
== msginfo
->to_folder
) {
383 copylist
= g_slist_prepend(copylist
, msginfo
);
387 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
390 copylist
= g_slist_reverse(copylist
);
391 folder_item_copy_msgs(dest
, copylist
);
392 g_slist_free(copylist
);
395 if (finished
== FALSE
) {
401 folder_item_update_thaw();
404 gchar
*procmsg_get_message_file_path(MsgInfo
*msginfo
)
406 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
408 return folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
411 gchar
*procmsg_get_message_file(MsgInfo
*msginfo
)
413 gchar
*filename
= NULL
;
415 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
417 filename
= folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
419 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
424 gchar
*procmsg_get_message_file_full(MsgInfo
*msginfo
, gboolean headers
, gboolean body
)
426 gchar
*filename
= NULL
;
428 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
430 filename
= folder_item_fetch_msg_full(msginfo
->folder
, msginfo
->msgnum
,
433 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
438 GSList
*procmsg_get_message_file_list(GSList
*mlist
)
440 GSList
*file_list
= NULL
;
442 MsgFileInfo
*fileinfo
;
445 while (mlist
!= NULL
) {
446 msginfo
= (MsgInfo
*)mlist
->data
;
447 file
= procmsg_get_message_file(msginfo
);
449 procmsg_message_file_list_free(file_list
);
452 fileinfo
= g_new(MsgFileInfo
, 1);
453 fileinfo
->msginfo
= procmsg_msginfo_new_ref(msginfo
);
454 fileinfo
->file
= file
;
455 fileinfo
->flags
= g_new(MsgFlags
, 1);
456 *fileinfo
->flags
= msginfo
->flags
;
457 file_list
= g_slist_prepend(file_list
, fileinfo
);
461 file_list
= g_slist_reverse(file_list
);
466 void procmsg_message_file_list_free(MsgInfoList
*file_list
)
469 MsgFileInfo
*fileinfo
;
471 for (cur
= file_list
; cur
!= NULL
; cur
= cur
->next
) {
472 fileinfo
= (MsgFileInfo
*)cur
->data
;
473 procmsg_msginfo_free(&(fileinfo
->msginfo
));
474 g_free(fileinfo
->file
);
475 g_free(fileinfo
->flags
);
479 g_slist_free(file_list
);
482 FILE *procmsg_open_message(MsgInfo
*msginfo
, gboolean skip_special_headers
)
487 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
489 file
= procmsg_get_message_file_path(msginfo
);
490 cm_return_val_if_fail(file
!= NULL
, NULL
);
492 if (!is_file_exist(file
)) {
494 file
= procmsg_get_message_file(msginfo
);
499 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
500 FILE_OP_ERROR(file
, "claws_fopen");
507 if (MSG_IS_QUEUED(msginfo
->flags
) || MSG_IS_DRAFT(msginfo
->flags
) ||
508 skip_special_headers
== TRUE
) {
511 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
513 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
514 strlen("X-Claws-End-Special-Headers:"))) ||
515 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
516 strlen("X-Sylpheed-End-Special-Headers:"))))
519 if (buf
[0] == '\r' || buf
[0] == '\n') break;
520 /* from other mailers */
521 if (!strncmp(buf
, "Date: ", 6)
522 || !strncmp(buf
, "To: ", 4)
523 || !strncmp(buf
, "From: ", 6)
524 || !strncmp(buf
, "Subject: ", 9)) {
534 gboolean
procmsg_msg_exist(MsgInfo
*msginfo
)
538 if (!msginfo
) return FALSE
;
540 ret
= !folder_item_is_msg_changed(msginfo
->folder
, msginfo
);
545 void procmsg_get_filter_keyword(MsgInfo
*msginfo
, gchar
**header
, gchar
**key
,
546 PrefsFilterType type
)
548 static HeaderEntry hentry
[] = {{"X-BeenThere:", NULL
, TRUE
},
549 {"X-ML-Name:", NULL
, TRUE
},
550 {"X-List:", NULL
, TRUE
},
551 {"X-Mailing-list:", NULL
, TRUE
},
552 {"List-Id:", NULL
, TRUE
},
553 {"X-Sequence:", NULL
, TRUE
},
554 {"Sender:", NULL
, TRUE
},
555 {"List-Post:", NULL
, TRUE
},
556 {NULL
, NULL
, FALSE
}};
562 H_X_MAILING_LIST
= 3,
571 cm_return_if_fail(msginfo
!= NULL
);
572 cm_return_if_fail(header
!= NULL
);
573 cm_return_if_fail(key
!= NULL
);
582 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
)
584 procheader_get_header_fields(fp
, hentry
);
587 #define SET_FILTER_KEY(hstr, idx) \
589 *header = g_strdup(hstr); \
590 *key = hentry[idx].body; \
591 hentry[idx].body = NULL; \
594 if (hentry
[H_LIST_ID
].body
!= NULL
) {
595 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID
);
596 extract_list_id_str(*key
);
597 } else if (hentry
[H_X_BEENTHERE
].body
!= NULL
) {
598 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE
);
599 } else if (hentry
[H_X_ML_NAME
].body
!= NULL
) {
600 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME
);
601 } else if (hentry
[H_X_LIST
].body
!= NULL
) {
602 SET_FILTER_KEY("header \"X-List\"", H_X_LIST
);
603 } else if (hentry
[H_X_MAILING_LIST
].body
!= NULL
) {
604 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST
);
605 } else if (hentry
[H_X_SEQUENCE
].body
!= NULL
) {
608 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE
);
611 while (*p
!= '\0' && !g_ascii_isspace(*p
)) p
++;
612 while (g_ascii_isspace(*p
)) p
++;
613 if (g_ascii_isdigit(*p
)) {
619 } else if (hentry
[H_SENDER
].body
!= NULL
) {
620 SET_FILTER_KEY("header \"Sender\"", H_SENDER
);
621 } else if (hentry
[H_LIST_POST
].body
!= NULL
) {
622 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST
);
623 } else if (msginfo
->to
) {
624 *header
= g_strdup("to");
625 *key
= g_strdup(msginfo
->to
);
626 } else if (msginfo
->subject
) {
627 *header
= g_strdup("subject");
628 *key
= g_strdup(msginfo
->subject
);
631 #undef SET_FILTER_KEY
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
);
666 static void procmsg_empty_trash(FolderItem
*trash
)
671 (trash
->stype
!= F_TRASH
&&
672 !folder_has_parent_of_type(trash
, F_TRASH
)))
675 if (trash
&& trash
->total_msgs
> 0) {
676 GSList
*mlist
= folder_item_get_msg_list(trash
);
678 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
679 MsgInfo
* msginfo
= (MsgInfo
*) cur
->data
;
680 if (MSG_IS_LOCKED(msginfo
->flags
)) {
681 procmsg_msginfo_free(&msginfo
);
684 if (msginfo
->total_size
!= 0 &&
685 msginfo
->size
!= (off_t
)msginfo
->total_size
)
686 partial_mark_for_delete(msginfo
);
688 procmsg_msginfo_free(&msginfo
);
691 folder_item_remove_all_msg(trash
);
694 if (!trash
->node
|| !trash
->node
->children
)
697 node
= trash
->node
->children
;
698 while (node
!= NULL
) {
700 procmsg_empty_trash(FOLDER_ITEM(node
->data
));
705 void procmsg_empty_all_trash(void)
710 for (cur
= folder_get_list(); cur
!= NULL
; cur
= cur
->next
) {
711 Folder
*folder
= FOLDER(cur
->data
);
712 trash
= folder
->trash
;
713 procmsg_empty_trash(trash
);
714 if (folder
->account
&& folder
->account
->set_trash_folder
&&
715 folder_find_item_from_identifier(folder
->account
->trash_folder
))
717 folder_find_item_from_identifier(folder
->account
->trash_folder
));
721 static PrefsAccount
*procmsg_get_account_from_file(const gchar
*file
)
723 PrefsAccount
*mailac
= NULL
;
727 static HeaderEntry qentry
[] = {{"S:", NULL
, FALSE
},
728 {"SSV:", NULL
, FALSE
},
730 {"NG:", NULL
, FALSE
},
731 {"MAID:", NULL
, FALSE
},
732 {"NAID:", NULL
, FALSE
},
733 {"SCF:", NULL
, FALSE
},
734 {"RMID:", NULL
, FALSE
},
735 {"FMID:", NULL
, FALSE
},
736 {"X-Claws-Privacy-System:", NULL
, FALSE
},
737 {"X-Claws-Encrypt:", NULL
, FALSE
},
738 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
739 {"X-Claws-End-Special-Headers", NULL
, FALSE
},
740 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
741 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
742 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
},
743 {NULL
, NULL
, FALSE
}};
745 cm_return_val_if_fail(file
!= NULL
, NULL
);
747 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
748 FILE_OP_ERROR(file
, "claws_fopen");
752 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
753 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
755 if (hnum
== Q_MAIL_ACCOUNT_ID
) {
756 mailac
= account_find_from_id(atoi(p
));
766 gchar
*procmsg_msginfo_get_avatar(MsgInfo
*msginfo
, gint type
)
770 if (!msginfo
|| !msginfo
->extradata
|| !msginfo
->extradata
->avatars
)
773 for (mia
= msginfo
->extradata
->avatars
; mia
; mia
= mia
->next
) {
774 MsgInfoAvatar
*avatar
= (MsgInfoAvatar
*)mia
->data
;
775 if (avatar
->avatar_id
== type
)
776 return avatar
->avatar_src
;
782 void procmsg_msginfo_add_avatar(MsgInfo
*msginfo
, gint type
, const gchar
*data
)
786 if (!msginfo
->extradata
)
787 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
789 av
= g_new0(MsgInfoAvatar
, 1);
790 av
->avatar_id
= type
;
791 av
->avatar_src
= g_strdup(data
);
793 msginfo
->extradata
->avatars
= g_slist_append(msginfo
->extradata
->avatars
, av
);
796 gchar
*procmsg_msginfo_get_identifier(MsgInfo
*msginfo
)
802 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
803 folder_id
= folder_item_get_identifier(msginfo
->folder
);
804 msgid
= msginfo
->msgid
;
806 id
= g_strconcat(folder_id
, G_DIR_SEPARATOR_S
, msgid
, NULL
);
813 MsgInfo
*procmsg_get_msginfo_from_identifier(const gchar
*id
)
815 gchar
*folder_id
= g_strdup(id
);
816 gchar
*separator
= strrchr(folder_id
, G_DIR_SEPARATOR
);
821 if (separator
== NULL
) {
827 msgid
= separator
+ 1;
829 item
= folder_find_item_from_identifier(folder_id
);
836 msginfo
= folder_item_get_msginfo_by_msgid(item
, msgid
);
842 static GSList
*procmsg_list_sort_by_account(FolderItem
*queue
, GSList
*list
)
844 GSList
*result
= NULL
;
846 PrefsAccount
*last_account
= NULL
;
849 gboolean nothing_to_sort
= TRUE
;
854 orig
= g_slist_copy(list
);
856 msg
= (MsgInfo
*)orig
->data
;
858 for (cur
= orig
; cur
; cur
= cur
->next
)
859 debug_print("sort before %s\n", ((MsgInfo
*)cur
->data
)->from
);
864 nothing_to_sort
= TRUE
;
868 PrefsAccount
*ac
= NULL
;
869 msg
= (MsgInfo
*)cur
->data
;
870 file
= folder_item_fetch_msg(queue
, msg
->msgnum
);
871 ac
= procmsg_get_account_from_file(file
);
874 if (last_account
== NULL
|| (ac
!= NULL
&& ac
== last_account
)) {
875 result
= g_slist_append(result
, msg
);
876 orig
= g_slist_remove(orig
, msg
);
878 nothing_to_sort
= FALSE
;
884 if (orig
&& g_slist_length(orig
)) {
885 if (!last_account
&& nothing_to_sort
) {
886 /* can't find an account for the rest of the list */
889 result
= g_slist_append(result
, cur
->data
);
900 for (cur
= result
; cur
; cur
= cur
->next
)
901 debug_print("sort after %s\n", ((MsgInfo
*)cur
->data
)->from
);
908 static gboolean
procmsg_is_last_for_account(FolderItem
*queue
, MsgInfo
*msginfo
, GSList
*elem
)
910 gchar
*file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
911 PrefsAccount
*ac
= procmsg_get_account_from_file(file
);
914 for (cur
= elem
; cur
; cur
= cur
->next
) {
915 MsgInfo
*cur_msginfo
= (MsgInfo
*)cur
->data
;
916 file
= folder_item_fetch_msg(queue
, cur_msginfo
->msgnum
);
918 if (cur_msginfo
!= msginfo
&& !MSG_IS_LOCKED(cur_msginfo
->flags
)) {
919 if (procmsg_get_account_from_file(file
) == ac
) {
930 static gboolean send_queue_lock
= FALSE
;
932 gboolean
procmsg_queue_lock(char **errstr
)
934 if (send_queue_lock
) {
935 /* Avoid having to translate two similar strings */
936 log_warning(LOG_PROTOCOL
, "%s\n", _("Already trying to send."));
938 if (*errstr
) g_free(*errstr
);
939 *errstr
= g_strdup_printf(_("Already trying to send."));
943 send_queue_lock
= TRUE
;
946 void procmsg_queue_unlock(void)
948 send_queue_lock
= FALSE
;
951 *\brief Send messages in queue
953 *\param queue Queue folder to process
954 *\param save_msgs Unused
956 *\return Number of messages sent, negative if an error occurred
957 * positive if no error occurred
959 gint
procmsg_send_queue(FolderItem
*queue
, gboolean save_msgs
, gchar
**errstr
)
961 gint sent
= 0, err
= 0;
963 GSList
*sorted_list
= NULL
;
966 if (!procmsg_queue_lock(errstr
)) {
967 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
968 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
973 queue
= folder_get_default_queue();
976 procmsg_queue_unlock();
981 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
982 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
984 folder_item_scan(queue
);
985 list
= folder_item_get_msg_list(queue
);
987 /* sort the list per sender account; this helps reusing the same SMTP server */
988 sorted_list
= procmsg_list_sort_by_account(queue
, list
);
990 for (elem
= sorted_list
; elem
!= NULL
; elem
= elem
->next
) {
994 msginfo
= (MsgInfo
*)(elem
->data
);
995 if (!MSG_IS_LOCKED(msginfo
->flags
) && !MSG_IS_DELETED(msginfo
->flags
)) {
996 file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
998 gboolean queued_removed
= FALSE
;
999 if (procmsg_send_message_queue_full(file
,
1000 !procmsg_is_last_for_account(queue
, msginfo
, elem
),
1001 errstr
, queue
, msginfo
->msgnum
, &queued_removed
) < 0) {
1002 g_warning("Sending queued message %d failed.",
1007 if (!queued_removed
)
1008 folder_item_remove_msg(queue
, msginfo
->msgnum
);
1013 /* FIXME: supposedly if only one message is locked, and queue
1014 * is being flushed, the following free says something like
1015 * "freeing msg ## in folder (nil)". */
1016 procmsg_msginfo_free(&msginfo
);
1019 g_slist_free(sorted_list
);
1020 folder_item_scan(queue
);
1022 if (queue
->node
&& queue
->node
->children
) {
1023 node
= queue
->node
->children
;
1024 while (node
!= NULL
) {
1027 send_queue_lock
= FALSE
;
1028 res
= procmsg_send_queue(FOLDER_ITEM(node
->data
), save_msgs
, errstr
);
1029 send_queue_lock
= TRUE
;
1037 procmsg_queue_unlock();
1039 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1040 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1042 return (err
!= 0 ? -err
: sent
);
1045 gboolean
procmsg_is_sending(void)
1047 return send_queue_lock
;
1051 *\brief Determine if a queue folder is empty
1053 *\param queue Queue folder to process
1055 *\return TRUE if the queue folder is empty, otherwise return FALSE
1057 gboolean
procmsg_queue_is_empty(FolderItem
*queue
)
1060 gboolean res
= FALSE
;
1062 queue
= folder_get_default_queue();
1063 cm_return_val_if_fail(queue
!= NULL
, TRUE
);
1065 folder_item_scan(queue
);
1066 list
= folder_item_get_msg_list(queue
);
1067 res
= (list
== NULL
);
1068 procmsg_msg_list_free(list
);
1072 if (queue
->node
&& queue
->node
->children
) {
1073 node
= queue
->node
->children
;
1074 while (node
!= NULL
) {
1076 if (!procmsg_queue_is_empty(FOLDER_ITEM(node
->data
)))
1085 gint
procmsg_remove_special_headers(const gchar
*in
, const gchar
*out
)
1088 gchar buf
[BUFFSIZE
];
1090 if ((fp
= claws_fopen(in
, "rb")) == NULL
) {
1091 FILE_OP_ERROR(in
, "claws_fopen");
1094 if ((outfp
= claws_fopen(out
, "wb")) == NULL
) {
1095 FILE_OP_ERROR(out
, "claws_fopen");
1099 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1101 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
1102 strlen("X-Claws-End-Special-Headers:"))) ||
1103 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
1104 strlen("X-Sylpheed-End-Special-Headers:"))))
1107 if (buf
[0] == '\r' || buf
[0] == '\n') break;
1108 /* from other mailers */
1109 if (!strncmp(buf
, "Date: ", 6)
1110 || !strncmp(buf
, "To: ", 4)
1111 || !strncmp(buf
, "From: ", 6)
1112 || !strncmp(buf
, "Subject: ", 9)) {
1117 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1118 if (claws_fputs(buf
, outfp
) == EOF
) {
1119 FILE_OP_ERROR(out
, "claws_fputs");
1120 claws_fclose(outfp
);
1125 claws_safe_fclose(outfp
);
1130 gint
procmsg_save_to_outbox(FolderItem
*outbox
, const gchar
*file
,
1134 MsgInfo
*msginfo
, *tmp_msginfo
;
1135 MsgFlags flag
= {0, 0};
1136 gchar
*outbox_path
= NULL
;
1139 debug_print("using default outbox\n");
1140 outbox
= folder_get_default_outbox();
1143 cm_return_val_if_fail(outbox
!= NULL
, -1);
1145 outbox_path
= folder_item_get_path(outbox
);
1146 debug_print("saving sent message to %s...\n", outbox_path
);
1147 g_free(outbox_path
);
1149 /* remove queueing headers */
1151 gchar tmp
[MAXPATHLEN
+ 1];
1153 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg.out.%08x",
1154 get_rc_dir(), G_DIR_SEPARATOR
, (guint
) rand());
1156 if (procmsg_remove_special_headers(file
, tmp
) !=0)
1159 folder_item_scan(outbox
);
1160 if ((num
= folder_item_add_msg(outbox
, tmp
, &flag
, TRUE
)) < 0) {
1161 g_warning("can't save message");
1166 folder_item_scan(outbox
);
1167 if ((num
= folder_item_add_msg
1168 (outbox
, file
, &flag
, FALSE
)) < 0) {
1169 g_warning("can't save message");
1173 msginfo
= folder_item_get_msginfo(outbox
, num
); /* refcnt++ */
1174 tmp_msginfo
= procmsg_msginfo_get_full_info(msginfo
); /* refcnt++ */
1175 if (msginfo
!= NULL
) {
1176 procmsg_msginfo_unset_flags(msginfo
, ~0, 0);
1177 procmsg_msginfo_free(&msginfo
); /* refcnt-- */
1178 /* tmp_msginfo == msginfo */
1179 if (tmp_msginfo
&& msginfo
->extradata
&&
1180 (msginfo
->extradata
->dispositionnotificationto
||
1181 msginfo
->extradata
->returnreceiptto
)) {
1182 procmsg_msginfo_set_flags(msginfo
, MSG_RETRCPT_SENT
, 0);
1184 procmsg_msginfo_free(&tmp_msginfo
); /* refcnt-- */
1191 MsgInfo
*procmsg_msginfo_new_ref(MsgInfo
*msginfo
)
1198 MsgInfo
*procmsg_msginfo_new(void)
1200 MsgInfo
*newmsginfo
;
1202 newmsginfo
= g_new0(MsgInfo
, 1);
1203 newmsginfo
->refcnt
= 1;
1208 static MsgInfoAvatar
*procmsg_msginfoavatar_copy(MsgInfoAvatar
*avatar
)
1210 MsgInfoAvatar
*newavatar
;
1212 if (avatar
== NULL
) return NULL
;
1214 newavatar
= g_new0(MsgInfoAvatar
, 1);
1215 newavatar
->avatar_id
= avatar
->avatar_id
;
1216 newavatar
->avatar_src
= g_strdup(avatar
->avatar_src
);
1221 static void procmsg_msginfoavatar_free(MsgInfoAvatar
*avatar
)
1223 if (avatar
!= NULL
) {
1224 if (avatar
->avatar_src
!= NULL
)
1225 g_free(avatar
->avatar_src
);
1230 MsgInfo
*procmsg_msginfo_copy(MsgInfo
*msginfo
)
1232 MsgInfo
*newmsginfo
;
1235 if (msginfo
== NULL
) return NULL
;
1237 newmsginfo
= g_new0(MsgInfo
, 1);
1239 newmsginfo
->refcnt
= 1;
1241 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1242 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1243 g_strdup(msginfo->mmb) : NULL
1258 MEMBDUP(newsgroups
);
1265 MEMBCOPY(to_folder
);
1267 if (msginfo
->extradata
) {
1268 newmsginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1269 if (msginfo
->extradata
->avatars
) {
1270 newmsginfo
->extradata
->avatars
= slist_copy_deep(msginfo
->extradata
->avatars
,
1271 (GCopyFunc
) procmsg_msginfoavatar_copy
);
1273 MEMBDUP(extradata
->dispositionnotificationto
);
1274 MEMBDUP(extradata
->returnreceiptto
);
1275 MEMBDUP(extradata
->partial_recv
);
1276 MEMBDUP(extradata
->account_server
);
1277 MEMBDUP(extradata
->account_login
);
1278 MEMBDUP(extradata
->list_post
);
1279 MEMBDUP(extradata
->list_subscribe
);
1280 MEMBDUP(extradata
->list_unsubscribe
);
1281 MEMBDUP(extradata
->list_help
);
1282 MEMBDUP(extradata
->list_archive
);
1283 MEMBDUP(extradata
->list_owner
);
1284 MEMBDUP(extradata
->resent_from
);
1287 refs
= msginfo
->references
;
1288 for (refs
= msginfo
->references
; refs
!= NULL
; refs
= refs
->next
) {
1289 newmsginfo
->references
= g_slist_prepend
1290 (newmsginfo
->references
, g_strdup(refs
->data
));
1292 newmsginfo
->references
= g_slist_reverse(newmsginfo
->references
);
1295 MEMBDUP(plaintext_file
);
1300 MsgInfo
*procmsg_msginfo_get_full_info_from_file(MsgInfo
*msginfo
, const gchar
*file
)
1302 MsgInfo
*full_msginfo
;
1304 if (msginfo
== NULL
) return NULL
;
1306 if (!file
|| !is_file_exist(file
)) {
1307 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1311 full_msginfo
= procheader_parse_file(file
, msginfo
->flags
, TRUE
, FALSE
);
1312 if (!full_msginfo
) return NULL
;
1314 msginfo
->total_size
= full_msginfo
->total_size
;
1315 msginfo
->planned_download
= full_msginfo
->planned_download
;
1317 if (full_msginfo
->extradata
) {
1318 if (!msginfo
->extradata
)
1319 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1320 if (!msginfo
->extradata
->list_post
)
1321 msginfo
->extradata
->list_post
= g_strdup(full_msginfo
->extradata
->list_post
);
1322 if (!msginfo
->extradata
->list_subscribe
)
1323 msginfo
->extradata
->list_subscribe
= g_strdup(full_msginfo
->extradata
->list_subscribe
);
1324 if (!msginfo
->extradata
->list_unsubscribe
)
1325 msginfo
->extradata
->list_unsubscribe
= g_strdup(full_msginfo
->extradata
->list_unsubscribe
);
1326 if (!msginfo
->extradata
->list_help
)
1327 msginfo
->extradata
->list_help
= g_strdup(full_msginfo
->extradata
->list_help
);
1328 if (!msginfo
->extradata
->list_archive
)
1329 msginfo
->extradata
->list_archive
= g_strdup(full_msginfo
->extradata
->list_archive
);
1330 if (!msginfo
->extradata
->list_owner
)
1331 msginfo
->extradata
->list_owner
= g_strdup(full_msginfo
->extradata
->list_owner
);
1332 if (!msginfo
->extradata
->avatars
)
1333 msginfo
->extradata
->avatars
= slist_copy_deep(full_msginfo
->extradata
->avatars
,
1334 (GCopyFunc
) procmsg_msginfoavatar_copy
);
1335 if (!msginfo
->extradata
->dispositionnotificationto
)
1336 msginfo
->extradata
->dispositionnotificationto
=
1337 g_strdup(full_msginfo
->extradata
->dispositionnotificationto
);
1338 if (!msginfo
->extradata
->returnreceiptto
)
1339 msginfo
->extradata
->returnreceiptto
= g_strdup
1340 (full_msginfo
->extradata
->returnreceiptto
);
1341 if (!msginfo
->extradata
->partial_recv
&& full_msginfo
->extradata
->partial_recv
)
1342 msginfo
->extradata
->partial_recv
= g_strdup
1343 (full_msginfo
->extradata
->partial_recv
);
1344 if (!msginfo
->extradata
->account_server
&& full_msginfo
->extradata
->account_server
)
1345 msginfo
->extradata
->account_server
= g_strdup
1346 (full_msginfo
->extradata
->account_server
);
1347 if (!msginfo
->extradata
->account_login
&& full_msginfo
->extradata
->account_login
)
1348 msginfo
->extradata
->account_login
= g_strdup
1349 (full_msginfo
->extradata
->account_login
);
1350 if (!msginfo
->extradata
->resent_from
&& full_msginfo
->extradata
->resent_from
)
1351 msginfo
->extradata
->resent_from
= g_strdup
1352 (full_msginfo
->extradata
->resent_from
);
1354 procmsg_msginfo_free(&full_msginfo
);
1356 return procmsg_msginfo_new_ref(msginfo
);
1359 MsgInfo
*procmsg_msginfo_get_full_info(MsgInfo
*msginfo
)
1361 MsgInfo
*full_msginfo
;
1364 if (msginfo
== NULL
) return NULL
;
1366 file
= procmsg_get_message_file_path(msginfo
);
1367 if (!file
|| !is_file_exist(file
)) {
1369 file
= procmsg_get_message_file(msginfo
);
1371 if (!file
|| !is_file_exist(file
)) {
1372 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1376 full_msginfo
= procmsg_msginfo_get_full_info_from_file(msginfo
, file
);
1378 return full_msginfo
;
1381 #define FREENULL(n) { g_free(n); n = NULL; }
1382 void procmsg_msginfo_free(MsgInfo
**msginfo_ptr
)
1384 MsgInfo
*msginfo
= *msginfo_ptr
;
1386 if (msginfo
== NULL
) return;
1389 if (msginfo
->refcnt
> 0)
1392 if (msginfo
->to_folder
) {
1393 msginfo
->to_folder
->op_count
--;
1394 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
1397 FREENULL(msginfo
->fromspace
);
1399 FREENULL(msginfo
->fromname
);
1401 FREENULL(msginfo
->date
);
1402 FREENULL(msginfo
->from
);
1403 FREENULL(msginfo
->to
);
1404 FREENULL(msginfo
->cc
);
1405 FREENULL(msginfo
->newsgroups
);
1406 FREENULL(msginfo
->subject
);
1407 FREENULL(msginfo
->msgid
);
1408 FREENULL(msginfo
->inreplyto
);
1409 FREENULL(msginfo
->xref
);
1411 if (msginfo
->extradata
) {
1412 if (msginfo
->extradata
->avatars
) {
1413 g_slist_foreach(msginfo
->extradata
->avatars
,
1414 (GFunc
)procmsg_msginfoavatar_free
,
1416 g_slist_free(msginfo
->extradata
->avatars
);
1417 msginfo
->extradata
->avatars
= NULL
;
1419 FREENULL(msginfo
->extradata
->returnreceiptto
);
1420 FREENULL(msginfo
->extradata
->dispositionnotificationto
);
1421 FREENULL(msginfo
->extradata
->list_post
);
1422 FREENULL(msginfo
->extradata
->list_subscribe
);
1423 FREENULL(msginfo
->extradata
->list_unsubscribe
);
1424 FREENULL(msginfo
->extradata
->list_help
);
1425 FREENULL(msginfo
->extradata
->list_archive
);
1426 FREENULL(msginfo
->extradata
->list_owner
);
1427 FREENULL(msginfo
->extradata
->partial_recv
);
1428 FREENULL(msginfo
->extradata
->account_server
);
1429 FREENULL(msginfo
->extradata
->account_login
);
1430 FREENULL(msginfo
->extradata
->resent_from
);
1431 FREENULL(msginfo
->extradata
);
1433 slist_free_strings_full(msginfo
->references
);
1434 msginfo
->references
= NULL
;
1435 g_slist_free(msginfo
->tags
);
1436 msginfo
->tags
= NULL
;
1438 FREENULL(msginfo
->plaintext_file
);
1441 *msginfo_ptr
= NULL
;
1445 guint
procmsg_msginfo_memusage(MsgInfo
*msginfo
)
1450 memusage
+= sizeof(MsgInfo
);
1451 if (msginfo
->fromname
)
1452 memusage
+= strlen(msginfo
->fromname
);
1454 memusage
+= strlen(msginfo
->date
);
1456 memusage
+= strlen(msginfo
->from
);
1458 memusage
+= strlen(msginfo
->to
);
1460 memusage
+= strlen(msginfo
->cc
);
1461 if (msginfo
->newsgroups
)
1462 memusage
+= strlen(msginfo
->newsgroups
);
1463 if (msginfo
->subject
)
1464 memusage
+= strlen(msginfo
->subject
);
1466 memusage
+= strlen(msginfo
->msgid
);
1467 if (msginfo
->inreplyto
)
1468 memusage
+= strlen(msginfo
->inreplyto
);
1470 for (tmp
= msginfo
->references
; tmp
; tmp
=tmp
->next
) {
1471 gchar
*r
= (gchar
*)tmp
->data
;
1472 memusage
+= r
?strlen(r
):0 + sizeof(GSList
);
1474 if (msginfo
->fromspace
)
1475 memusage
+= strlen(msginfo
->fromspace
);
1477 for (tmp
= msginfo
->tags
; tmp
; tmp
=tmp
->next
) {
1478 memusage
+= sizeof(GSList
);
1480 if (msginfo
->extradata
) {
1481 memusage
+= sizeof(MsgInfoExtraData
);
1482 if (msginfo
->extradata
->avatars
) {
1483 for (tmp
= msginfo
->extradata
->avatars
; tmp
; tmp
= tmp
->next
) {
1484 MsgInfoAvatar
*avt
= (MsgInfoAvatar
*)tmp
->data
;
1485 memusage
+= (avt
->avatar_src
)? strlen(avt
->avatar_src
): 0;
1486 memusage
+= sizeof(MsgInfoAvatar
) + sizeof(GSList
);
1489 if (msginfo
->extradata
->dispositionnotificationto
)
1490 memusage
+= strlen(msginfo
->extradata
->dispositionnotificationto
);
1491 if (msginfo
->extradata
->returnreceiptto
)
1492 memusage
+= strlen(msginfo
->extradata
->returnreceiptto
);
1494 if (msginfo
->extradata
->partial_recv
)
1495 memusage
+= strlen(msginfo
->extradata
->partial_recv
);
1496 if (msginfo
->extradata
->account_server
)
1497 memusage
+= strlen(msginfo
->extradata
->account_server
);
1498 if (msginfo
->extradata
->account_login
)
1499 memusage
+= strlen(msginfo
->extradata
->account_login
);
1500 if (msginfo
->extradata
->resent_from
)
1501 memusage
+= strlen(msginfo
->extradata
->resent_from
);
1503 if (msginfo
->extradata
->list_post
)
1504 memusage
+= strlen(msginfo
->extradata
->list_post
);
1505 if (msginfo
->extradata
->list_subscribe
)
1506 memusage
+= strlen(msginfo
->extradata
->list_subscribe
);
1507 if (msginfo
->extradata
->list_unsubscribe
)
1508 memusage
+= strlen(msginfo
->extradata
->list_unsubscribe
);
1509 if (msginfo
->extradata
->list_help
)
1510 memusage
+= strlen(msginfo
->extradata
->list_help
);
1511 if (msginfo
->extradata
->list_archive
)
1512 memusage
+= strlen(msginfo
->extradata
->list_archive
);
1513 if (msginfo
->extradata
->list_owner
)
1514 memusage
+= strlen(msginfo
->extradata
->list_owner
);
1519 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
1520 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1522 static HeaderEntry qentry
[] = {
1523 {"S:", NULL
, FALSE
}, /* 0 */
1524 {"SSV:", NULL
, FALSE
},
1525 {"R:", NULL
, FALSE
},
1526 {"NG:", NULL
, FALSE
},
1527 {"MAID:", NULL
, FALSE
},
1528 {"NAID:", NULL
, FALSE
}, /* 5 */
1529 {"SCF:", NULL
, FALSE
},
1530 {"RMID:", NULL
, FALSE
},
1531 {"FMID:", NULL
, FALSE
},
1532 {"X-Claws-Privacy-System:", NULL
, FALSE
},
1533 {"X-Claws-Encrypt:", NULL
, FALSE
}, /* 10 */
1534 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
1535 {"X-Claws-End-Special-Headers:", NULL
, FALSE
},
1536 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
1537 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
1538 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
}, /* 15 */
1539 {"X-Sylpheed-End-Special-Headers:", NULL
, FALSE
},
1540 {NULL
, NULL
, FALSE
}};
1543 gint mailval
= 0, newsval
= 0;
1545 gchar
*smtpserver
= NULL
;
1546 GSList
*to_list
= NULL
;
1547 GSList
*newsgroup_list
= NULL
;
1548 gchar
*savecopyfolder
= NULL
;
1549 gchar
*replymessageid
= NULL
;
1550 gchar
*fwdmessageid
= NULL
;
1553 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
1554 gboolean encrypt
= FALSE
;
1557 cm_return_val_if_fail(file
!= NULL
, -1);
1559 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
1560 FILE_OP_ERROR(file
, "claws_fopen");
1562 if (*errstr
) g_free(*errstr
);
1563 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1568 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
1569 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
1577 if (smtpserver
== NULL
)
1578 smtpserver
= g_strdup(p
);
1581 to_list
= address_list_append(to_list
, p
);
1584 newsgroup_list
= newsgroup_list_append(newsgroup_list
, p
);
1586 case Q_MAIL_ACCOUNT_ID
:
1587 mailac
= account_find_from_id(atoi(p
));
1589 case Q_NEWS_ACCOUNT_ID
:
1590 newsac
= account_find_from_id(atoi(p
));
1592 case Q_SAVE_COPY_FOLDER
:
1593 if (savecopyfolder
== NULL
)
1594 savecopyfolder
= g_strdup(p
);
1596 case Q_REPLY_MESSAGE_ID
:
1597 if (replymessageid
== NULL
)
1598 replymessageid
= g_strdup(p
);
1600 case Q_FWD_MESSAGE_ID
:
1601 if (fwdmessageid
== NULL
)
1602 fwdmessageid
= g_strdup(p
);
1610 case Q_CLAWS_HDRS_OLD
:
1611 /* end of special headers reached */
1613 goto send_mail
; /* can't "break;break;" */
1619 filepos
= ftell(fp
);
1621 FILE_OP_ERROR(file
, "ftell");
1623 if (*errstr
) g_free(*errstr
);
1624 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1630 debug_print("Sending message by mail\n");
1633 if (*errstr
) g_free(*errstr
);
1634 *errstr
= g_strdup_printf(_("Queued message header is broken."));
1637 } else if (mailac
&& mailac
->use_mail_command
&&
1638 mailac
->mail_command
&& (* mailac
->mail_command
)) {
1639 mailval
= send_message_local(mailac
->mail_command
, fp
);
1642 mailac
= account_find_from_smtp_server(from
, smtpserver
);
1644 g_warning("Account not found. "
1645 "Using current account...");
1646 mailac
= cur_account
;
1651 mailval
= send_message_smtp_full(mailac
, to_list
, fp
, keep_session
);
1652 if (mailval
== -1 && errstr
) {
1653 if (*errstr
) g_free(*errstr
);
1654 *errstr
= g_strdup_printf(_("An error happened during SMTP session."));
1657 PrefsAccount tmp_ac
;
1659 g_warning("Account not found.");
1661 memset(&tmp_ac
, 0, sizeof(PrefsAccount
));
1662 tmp_ac
.address
= from
;
1663 tmp_ac
.smtp_server
= smtpserver
;
1664 tmp_ac
.smtpport
= SMTP_PORT
;
1665 mailval
= send_message_smtp(&tmp_ac
, to_list
, fp
);
1666 if (mailval
== -1 && errstr
) {
1667 if (*errstr
) g_free(*errstr
);
1668 *errstr
= g_strdup_printf(_("No specific account has been found to "
1669 "send, and an error happened during SMTP session."));
1673 } else if (!to_list
&& !newsgroup_list
) {
1675 if (*errstr
) g_free(*errstr
);
1676 *errstr
= g_strdup(_("Couldn't determine sending information. "
1677 "Maybe the email hasn't been generated by Claws Mail."));
1682 if (fseek(fp
, filepos
, SEEK_SET
) < 0) {
1683 FILE_OP_ERROR(file
, "fseek");
1687 if (newsgroup_list
&& newsac
&& (mailval
== 0)) {
1690 gchar buf
[BUFFSIZE
];
1693 /* write to temporary file */
1694 tmp
= g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1695 G_DIR_SEPARATOR
, file
);
1696 if ((tmpfp
= claws_fopen(tmp
, "wb")) == NULL
) {
1697 FILE_OP_ERROR(tmp
, "claws_fopen");
1699 alertpanel_error(_("Couldn't create temporary file for news sending."));
1701 if (change_file_mode_rw(tmpfp
, tmp
) < 0) {
1702 FILE_OP_ERROR(tmp
, "chmod");
1703 g_warning("can't change file mode");
1706 while ((newsval
== 0) && claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1707 if (claws_fputs(buf
, tmpfp
) == EOF
) {
1708 FILE_OP_ERROR(tmp
, "claws_fputs");
1711 if (*errstr
) g_free(*errstr
);
1712 *errstr
= g_strdup_printf(_("Error when writing temporary file for news sending."));
1716 claws_safe_fclose(tmpfp
);
1719 debug_print("Sending message by news\n");
1721 folder
= FOLDER(newsac
->folder
);
1723 newsval
= news_post(folder
, tmp
);
1724 if (newsval
< 0 && errstr
) {
1725 if (*errstr
) g_free(*errstr
);
1726 *errstr
= g_strdup_printf(_("Error occurred while posting the message to %s."),
1727 newsac
->nntp_server
);
1737 /* update session statistics */
1738 if (mailval
== 0 && newsval
== 0) {
1739 /* update session stats */
1741 session_stats
.replied
++;
1742 else if (fwdmessageid
)
1743 session_stats
.forwarded
++;
1745 session_stats
.sent
++;
1748 /* save message to outbox */
1749 if (mailval
== 0 && newsval
== 0 && savecopyfolder
) {
1750 debug_print("saving sent message to %s...\n", savecopyfolder
);
1752 if (!encrypt
|| !mailac
->save_encrypted_as_clear_text
) {
1753 outbox
= folder_find_item_from_identifier(savecopyfolder
);
1756 outbox
= folder_get_default_outbox();
1757 if (outbox
!= NULL
) {
1758 id
= folder_item_get_identifier(outbox
);
1759 debug_print("%s not found, using %s\n", savecopyfolder
, id
);
1762 debug_print("could not find outbox\n");
1765 /* Mail was not saved to outbox before encrypting, save it now. */
1766 gboolean saved
= FALSE
;
1767 *queued_removed
= FALSE
;
1768 if (queue
&& msgnum
> 0) {
1769 MsgInfo
*queued_mail
= folder_item_get_msginfo(queue
, msgnum
);
1770 if (folder_item_move_msg(outbox
, queued_mail
) >= 0) {
1771 debug_print("moved queued mail %d to sent folder\n", msgnum
);
1773 *queued_removed
= TRUE
;
1774 } else if (folder_item_copy_msg(outbox
, queued_mail
) >= 0) {
1775 debug_print("copied queued mail %d to sent folder\n", msgnum
);
1778 procmsg_msginfo_free(&queued_mail
);
1781 debug_print("resaving queued mail to sent folder\n");
1782 procmsg_save_to_outbox(outbox
, file
, TRUE
);
1787 if (replymessageid
!= NULL
|| fwdmessageid
!= NULL
) {
1791 if (replymessageid
!= NULL
)
1792 tokens
= g_strsplit(replymessageid
, "\t", 0);
1794 tokens
= g_strsplit(fwdmessageid
, "\t", 0);
1795 item
= folder_find_item_from_identifier(tokens
[0]);
1797 /* check if queued message has valid folder and message id */
1798 if (item
!= NULL
&& tokens
[2] != NULL
) {
1801 msginfo
= folder_item_get_msginfo(item
, atoi(tokens
[1]));
1803 /* check if referring message exists and has a message id */
1804 if ((msginfo
!= NULL
) &&
1805 (msginfo
->msgid
!= NULL
) &&
1806 (strcmp(msginfo
->msgid
, tokens
[2]) != 0)) {
1807 procmsg_msginfo_free(&msginfo
);
1811 if (msginfo
== NULL
) {
1812 msginfo
= folder_item_get_msginfo_by_msgid(item
, tokens
[2]);
1815 if (msginfo
!= NULL
) {
1816 if (replymessageid
!= NULL
) {
1817 MsgPermFlags to_unset
= 0;
1819 if (prefs_common
.mark_as_read_on_new_window
)
1820 to_unset
= (MSG_NEW
|MSG_UNREAD
);
1822 procmsg_msginfo_unset_flags(msginfo
, to_unset
, 0);
1823 procmsg_msginfo_set_flags(msginfo
, MSG_REPLIED
, 0);
1825 procmsg_msginfo_set_flags(msginfo
, MSG_FORWARDED
, 0);
1827 procmsg_msginfo_free(&msginfo
);
1835 slist_free_strings_full(to_list
);
1836 slist_free_strings_full(newsgroup_list
);
1837 g_free(savecopyfolder
);
1838 g_free(replymessageid
);
1839 g_free(fwdmessageid
);
1841 return (newsval
!= 0 ? newsval
: mailval
);
1844 gint
procmsg_send_message_queue(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1846 gint result
= procmsg_send_message_queue_full(file
, FALSE
, errstr
, queue
, msgnum
, queued_removed
);
1847 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1848 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1852 gint
procmsg_send_message_queue_with_lock(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1855 if (procmsg_queue_lock(errstr
)) {
1856 val
= procmsg_send_message_queue(file
, errstr
, queue
, msgnum
, queued_removed
);
1857 procmsg_queue_unlock();
1863 static void update_folder_msg_counts(FolderItem
*item
, MsgInfo
*msginfo
, MsgPermFlags old_flags
)
1865 MsgPermFlags new_flags
= msginfo
->flags
.perm_flags
;
1868 if (!(old_flags
& MSG_NEW
) && (new_flags
& MSG_NEW
)) {
1872 if ((old_flags
& MSG_NEW
) && !(new_flags
& MSG_NEW
)) {
1877 if (!(old_flags
& MSG_UNREAD
) && (new_flags
& MSG_UNREAD
)) {
1878 item
->unread_msgs
++;
1879 if (procmsg_msg_has_marked_parent(msginfo
))
1880 item
->unreadmarked_msgs
++;
1883 if ((old_flags
& MSG_UNREAD
) && !(new_flags
& MSG_UNREAD
)) {
1884 item
->unread_msgs
--;
1885 if (procmsg_msg_has_marked_parent(msginfo
))
1886 item
->unreadmarked_msgs
--;
1890 if (!(old_flags
& MSG_MARKED
) && (new_flags
& MSG_MARKED
)) {
1891 procmsg_update_unread_children(msginfo
, TRUE
);
1892 item
->marked_msgs
++;
1895 if ((old_flags
& MSG_MARKED
) && !(new_flags
& MSG_MARKED
)) {
1896 procmsg_update_unread_children(msginfo
, FALSE
);
1897 item
->marked_msgs
--;
1900 if (!(old_flags
& MSG_REPLIED
) && (new_flags
& MSG_REPLIED
)) {
1901 item
->replied_msgs
++;
1904 if ((old_flags
& MSG_REPLIED
) && !(new_flags
& MSG_REPLIED
)) {
1905 item
->replied_msgs
--;
1908 if (!(old_flags
& MSG_FORWARDED
) && (new_flags
& MSG_FORWARDED
)) {
1909 item
->forwarded_msgs
++;
1912 if ((old_flags
& MSG_FORWARDED
) && !(new_flags
& MSG_FORWARDED
)) {
1913 item
->forwarded_msgs
--;
1916 if (!(old_flags
& MSG_LOCKED
) && (new_flags
& MSG_LOCKED
)) {
1917 item
->locked_msgs
++;
1920 if ((old_flags
& MSG_LOCKED
) && !(new_flags
& MSG_LOCKED
)) {
1921 item
->locked_msgs
--;
1924 if ((old_flags
& MSG_IGNORE_THREAD
) && !(new_flags
& MSG_IGNORE_THREAD
)) {
1925 item
->ignored_msgs
--;
1928 if (!(old_flags
& MSG_IGNORE_THREAD
) && (new_flags
& MSG_IGNORE_THREAD
)) {
1929 item
->ignored_msgs
++;
1932 if ((old_flags
& MSG_WATCH_THREAD
) && !(new_flags
& MSG_WATCH_THREAD
)) {
1933 item
->watched_msgs
--;
1936 if (!(old_flags
& MSG_WATCH_THREAD
) && (new_flags
& MSG_WATCH_THREAD
)) {
1937 item
->watched_msgs
++;
1941 void procmsg_msginfo_set_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
1944 MsgInfoUpdate msginfo_update
;
1945 MsgPermFlags perm_flags_new
, perm_flags_old
;
1946 MsgTmpFlags tmp_flags_old
;
1948 cm_return_if_fail(msginfo
!= NULL
);
1949 item
= msginfo
->folder
;
1950 cm_return_if_fail(item
!= NULL
);
1952 debug_print("Setting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
1954 /* Perm Flags handling */
1955 perm_flags_old
= msginfo
->flags
.perm_flags
;
1956 perm_flags_new
= msginfo
->flags
.perm_flags
| perm_flags
;
1957 if ((perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
1958 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
1960 if ((perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
1961 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
1964 if (perm_flags_old
!= perm_flags_new
) {
1965 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
1967 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
1968 summary_update_unread(mainwindow_get_mainwindow()->summaryview
, NULL
);
1971 /* Tmp flags handling */
1972 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
1973 msginfo
->flags
.tmp_flags
|= tmp_flags
;
1975 /* update notification */
1976 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
1977 msginfo_update
.msginfo
= msginfo
;
1978 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
1979 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
1980 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
1984 void procmsg_msginfo_unset_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
1987 MsgInfoUpdate msginfo_update
;
1988 MsgPermFlags perm_flags_new
, perm_flags_old
;
1989 MsgTmpFlags tmp_flags_old
;
1991 cm_return_if_fail(msginfo
!= NULL
);
1992 item
= msginfo
->folder
;
1993 cm_return_if_fail(item
!= NULL
);
1995 debug_print("Unsetting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
1997 /* Perm Flags handling */
1998 perm_flags_old
= msginfo
->flags
.perm_flags
;
1999 perm_flags_new
= msginfo
->flags
.perm_flags
& ~perm_flags
;
2001 if (perm_flags_old
!= perm_flags_new
) {
2002 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2004 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2007 /* Tmp flags hanlding */
2008 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2009 msginfo
->flags
.tmp_flags
&= ~tmp_flags
;
2011 /* update notification */
2012 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2013 msginfo_update
.msginfo
= msginfo
;
2014 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2015 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2016 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2020 void procmsg_msginfo_change_flags(MsgInfo
*msginfo
,
2021 MsgPermFlags add_perm_flags
, MsgTmpFlags add_tmp_flags
,
2022 MsgPermFlags rem_perm_flags
, MsgTmpFlags rem_tmp_flags
)
2025 MsgInfoUpdate msginfo_update
;
2026 MsgPermFlags perm_flags_new
, perm_flags_old
;
2027 MsgTmpFlags tmp_flags_old
;
2029 cm_return_if_fail(msginfo
!= NULL
);
2030 item
= msginfo
->folder
;
2031 cm_return_if_fail(item
!= NULL
);
2033 debug_print("Changing flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
2035 /* Perm Flags handling */
2036 perm_flags_old
= msginfo
->flags
.perm_flags
;
2037 perm_flags_new
= (msginfo
->flags
.perm_flags
& ~rem_perm_flags
) | add_perm_flags
;
2038 if ((add_perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
2039 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
2041 if ((add_perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
2042 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
2045 if (perm_flags_old
!= perm_flags_new
) {
2046 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2048 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2052 /* Tmp flags handling */
2053 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2054 msginfo
->flags
.tmp_flags
&= ~rem_tmp_flags
;
2055 msginfo
->flags
.tmp_flags
|= add_tmp_flags
;
2057 /* update notification */
2058 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2059 msginfo_update
.msginfo
= msginfo
;
2060 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2061 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2062 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2067 *\brief check for flags (e.g. mark) in prior msgs of current thread
2069 *\param info Current message
2070 *\param perm_flags Flags to be checked
2071 *\param parentmsgs Hash of prior msgs to avoid loops
2073 *\return gboolean TRUE if perm_flags are found
2075 static gboolean
procmsg_msg_has_flagged_parent_real(MsgInfo
*info
,
2076 MsgPermFlags perm_flags
, GHashTable
*parentmsgs
)
2080 cm_return_val_if_fail(info
!= NULL
, FALSE
);
2082 if (info
!= NULL
&& info
->folder
!= NULL
&& info
->inreplyto
!= NULL
) {
2083 tmp
= folder_item_get_msginfo_by_msgid(info
->folder
,
2085 if (tmp
&& (tmp
->flags
.perm_flags
& perm_flags
)) {
2086 procmsg_msginfo_free(&tmp
);
2088 } else if (tmp
!= NULL
) {
2091 if (g_hash_table_lookup(parentmsgs
, info
)) {
2092 debug_print("loop detected: %d\n",
2096 g_hash_table_insert(parentmsgs
, info
, "1");
2097 result
= procmsg_msg_has_flagged_parent_real(
2098 tmp
, perm_flags
, parentmsgs
);
2100 procmsg_msginfo_free(&tmp
);
2110 *\brief Callback for cleaning up hash of parentmsgs
2112 static gboolean
parentmsgs_hash_remove(gpointer key
,
2120 *\brief Set up list of parentmsgs
2121 * See procmsg_msg_has_flagged_parent_real()
2123 gboolean
procmsg_msg_has_flagged_parent(MsgInfo
*info
, MsgPermFlags perm_flags
)
2126 static GHashTable
*parentmsgs
= NULL
;
2128 if (parentmsgs
== NULL
)
2129 parentmsgs
= g_hash_table_new(NULL
, NULL
);
2131 result
= procmsg_msg_has_flagged_parent_real(info
, perm_flags
, parentmsgs
);
2132 g_hash_table_foreach_remove(parentmsgs
, parentmsgs_hash_remove
, NULL
);
2138 *\brief Check if msgs prior in thread are marked
2139 * See procmsg_msg_has_flagged_parent_real()
2141 gboolean
procmsg_msg_has_marked_parent(MsgInfo
*info
)
2143 return procmsg_msg_has_flagged_parent(info
, MSG_MARKED
);
2147 static GSList
*procmsg_find_children_func(MsgInfo
*info
,
2148 GSList
*children
, GSList
*all
)
2152 cm_return_val_if_fail(info
!=NULL
, children
);
2153 if (info
->msgid
== NULL
)
2156 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2157 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2158 if (tmp
->inreplyto
&& !strcmp(tmp
->inreplyto
, info
->msgid
)) {
2159 /* Check if message is already in the list */
2160 if ((children
== NULL
) ||
2161 (g_slist_index(children
, tmp
) == -1)) {
2162 children
= g_slist_prepend(children
,
2163 procmsg_msginfo_new_ref(tmp
));
2164 children
= procmsg_find_children_func(tmp
,
2173 static GSList
*procmsg_find_children (MsgInfo
*info
)
2178 cm_return_val_if_fail(info
!=NULL
, NULL
);
2179 all
= folder_item_get_msg_list(info
->folder
);
2180 children
= procmsg_find_children_func(info
, NULL
, all
);
2181 if (children
!= NULL
) {
2182 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2183 /* this will not free the used pointers
2184 created with procmsg_msginfo_new_ref */
2185 procmsg_msginfo_free((MsgInfo
**)&(cur
->data
));
2193 static void procmsg_update_unread_children(MsgInfo
*info
, gboolean newly_marked
)
2195 GSList
*children
= procmsg_find_children(info
);
2197 for (cur
= children
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2198 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2199 if(MSG_IS_UNREAD(tmp
->flags
) && !MSG_IS_IGNORE_THREAD(tmp
->flags
)) {
2201 info
->folder
->unreadmarked_msgs
++;
2203 info
->folder
->unreadmarked_msgs
--;
2204 folder_item_update(info
->folder
, F_ITEM_UPDATE_MSGCNT
);
2206 procmsg_msginfo_free(&tmp
);
2208 g_slist_free(children
);
2212 * Set the destination folder for a copy or move operation
2214 * \param msginfo The message which's destination folder is changed
2215 * \param to_folder The destination folder for the operation
2217 void procmsg_msginfo_set_to_folder(MsgInfo
*msginfo
, FolderItem
*to_folder
)
2219 if(msginfo
->to_folder
!= NULL
) {
2220 msginfo
->to_folder
->op_count
--;
2221 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2223 msginfo
->to_folder
= to_folder
;
2224 if(to_folder
!= NULL
) {
2225 to_folder
->op_count
++;
2226 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2231 * Apply filtering actions to the msginfo
2233 * \param msginfo The MsgInfo describing the message that should be filtered
2234 * \return TRUE if the message was moved and MsgInfo is now invalid,
2237 static gboolean
procmsg_msginfo_filter(MsgInfo
*msginfo
, PrefsAccount
* ac_prefs
)
2239 MailFilteringData mail_filtering_data
;
2241 mail_filtering_data
.msginfo
= msginfo
;
2242 mail_filtering_data
.msglist
= NULL
;
2243 mail_filtering_data
.filtered
= NULL
;
2244 mail_filtering_data
.unfiltered
= NULL
;
2245 mail_filtering_data
.account
= ac_prefs
;
2247 if (!ac_prefs
|| ac_prefs
->filterhook_on_recv
)
2248 if (hooks_invoke(MAIL_FILTERING_HOOKLIST
, &mail_filtering_data
))
2251 /* filter if enabled in prefs or move to inbox if not */
2252 if((filtering_rules
!= NULL
) &&
2253 filter_message_by_msginfo(filtering_rules
, msginfo
, ac_prefs
,
2254 FILTERING_INCORPORATION
, NULL
)) {
2261 void procmsg_msglist_filter(GSList
*list
, PrefsAccount
*ac
,
2262 GSList
**filtered
, GSList
**unfiltered
,
2265 GSList
*cur
, *to_do
= NULL
;
2266 gint total
= 0, curnum
= 0;
2267 MailFilteringData mail_filtering_data
;
2269 cm_return_if_fail(filtered
!= NULL
);
2270 cm_return_if_fail(unfiltered
!= NULL
);
2278 total
= g_slist_length(list
);
2282 *unfiltered
= g_slist_copy(list
);
2286 statusbar_print_all(_("Filtering messages...\n"));
2288 mail_filtering_data
.msginfo
= NULL
;
2289 mail_filtering_data
.msglist
= list
;
2290 mail_filtering_data
.filtered
= NULL
;
2291 mail_filtering_data
.unfiltered
= NULL
;
2292 mail_filtering_data
.account
= ac
;
2294 if (!ac
|| ac
->filterhook_on_recv
)
2295 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST
, &mail_filtering_data
);
2297 if (mail_filtering_data
.filtered
== NULL
&&
2298 mail_filtering_data
.unfiltered
== NULL
) {
2299 /* nothing happened */
2300 debug_print(MAIL_LISTFILTERING_HOOKLIST
" did nothing. filtering whole list normally.\n");
2303 if (mail_filtering_data
.filtered
!= NULL
) {
2304 /* keep track of what's been filtered by the hooks */
2305 debug_print(MAIL_LISTFILTERING_HOOKLIST
" filtered some stuff. total %d filtered %d unfilt %d.\n",
2306 g_slist_length(list
),
2307 g_slist_length(mail_filtering_data
.filtered
),
2308 g_slist_length(mail_filtering_data
.unfiltered
));
2310 *filtered
= g_slist_copy(mail_filtering_data
.filtered
);
2312 if (mail_filtering_data
.unfiltered
!= NULL
) {
2313 /* what the hooks didn't handle will go in filtered or
2314 * unfiltered in the next loop */
2315 debug_print(MAIL_LISTFILTERING_HOOKLIST
" left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2316 g_slist_length(list
),
2317 g_slist_length(mail_filtering_data
.filtered
),
2318 g_slist_length(mail_filtering_data
.unfiltered
));
2319 to_do
= mail_filtering_data
.unfiltered
;
2322 for (cur
= to_do
; cur
; cur
= cur
->next
) {
2323 MsgInfo
*info
= (MsgInfo
*)cur
->data
;
2324 if (procmsg_msginfo_filter(info
, ac
))
2325 *filtered
= g_slist_prepend(*filtered
, info
);
2327 *unfiltered
= g_slist_prepend(*unfiltered
, info
);
2328 statusbar_progress_all(curnum
++, total
, prefs_common
.statusbar_update_step
);
2331 g_slist_free(mail_filtering_data
.filtered
);
2332 g_slist_free(mail_filtering_data
.unfiltered
);
2334 *filtered
= g_slist_reverse(*filtered
);
2335 *unfiltered
= g_slist_reverse(*unfiltered
);
2337 statusbar_progress_all(0,0,0);
2338 statusbar_pop_all();
2341 MsgInfo
*procmsg_msginfo_new_from_mimeinfo(MsgInfo
*src_msginfo
, MimeInfo
*mimeinfo
)
2343 MsgInfo
*tmp_msginfo
= NULL
;
2344 MsgFlags flags
= {0, 0};
2345 gchar
*tmpfile
= get_tmp_file();
2346 FILE *fp
= claws_fopen(tmpfile
, "wb");
2348 if (!mimeinfo
|| mimeinfo
->type
!= MIMETYPE_MESSAGE
||
2349 g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
2350 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2357 if (fp
&& procmime_write_mimeinfo(mimeinfo
, fp
) >= 0) {
2358 claws_safe_fclose(fp
);
2360 tmp_msginfo
= procheader_parse_file(
2365 claws_safe_fclose(fp
);
2367 if (tmp_msginfo
!= NULL
) {
2369 tmp_msginfo
->folder
= src_msginfo
->folder
;
2370 tmp_msginfo
->plaintext_file
= g_strdup(tmpfile
);
2372 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2380 static GSList
*spam_learners
= NULL
;
2382 void procmsg_register_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2384 if (!g_slist_find(spam_learners
, learn_func
))
2385 spam_learners
= g_slist_append(spam_learners
, learn_func
);
2386 if (mainwindow_get_mainwindow()) {
2387 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2388 summary_set_menu_sensitive(
2389 mainwindow_get_mainwindow()->summaryview
);
2390 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2394 void procmsg_unregister_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2396 spam_learners
= g_slist_remove(spam_learners
, learn_func
);
2397 if (mainwindow_get_mainwindow()) {
2398 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2399 summary_set_menu_sensitive(
2400 mainwindow_get_mainwindow()->summaryview
);
2401 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2405 gboolean
procmsg_spam_can_learn(void)
2407 return g_slist_length(spam_learners
) > 0;
2410 int procmsg_spam_learner_learn (MsgInfo
*info
, GSList
*list
, gboolean spam
)
2412 GSList
*cur
= spam_learners
;
2414 for (; cur
; cur
= cur
->next
) {
2415 int ((*func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
)) = cur
->data
;
2416 ret
|= func(info
, list
, spam
);
2421 static gchar
*spam_folder_item
= NULL
;
2422 static FolderItem
* (*procmsg_spam_get_folder_func
)(MsgInfo
*msginfo
) = NULL
;
2423 void procmsg_spam_set_folder (const char *item_identifier
, FolderItem
*(*spam_get_folder_func
)(MsgInfo
*info
))
2425 g_free(spam_folder_item
);
2426 if (item_identifier
)
2427 spam_folder_item
= g_strdup(item_identifier
);
2429 spam_folder_item
= NULL
;
2430 if (spam_get_folder_func
!= NULL
)
2431 procmsg_spam_get_folder_func
= spam_get_folder_func
;
2433 procmsg_spam_get_folder_func
= NULL
;
2436 FolderItem
*procmsg_spam_get_folder (MsgInfo
*msginfo
)
2438 FolderItem
*item
= NULL
;
2440 if (procmsg_spam_get_folder_func
)
2441 item
= procmsg_spam_get_folder_func(msginfo
);
2442 if (item
== NULL
&& spam_folder_item
)
2443 item
= folder_find_item_from_identifier(spam_folder_item
);
2445 item
= folder_get_default_trash();
2449 static void item_has_queued_mails(FolderItem
*item
, gpointer data
)
2451 gboolean
*result
= (gboolean
*)data
;
2452 if (*result
== TRUE
)
2454 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
2455 if (item
->total_msgs
== 0)
2458 GSList
*msglist
= folder_item_get_msg_list(item
);
2460 for (cur
= msglist
; cur
; cur
= cur
->next
) {
2461 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
2462 if (!MSG_IS_DELETED(msginfo
->flags
) &&
2463 !MSG_IS_LOCKED(msginfo
->flags
)) {
2468 procmsg_msg_list_free(msglist
);
2473 gboolean
procmsg_have_queued_mails_fast (void)
2475 gboolean result
= FALSE
;
2476 folder_func_to_all_folders(item_has_queued_mails
, &result
);
2480 static void item_has_trashed_mails(FolderItem
*item
, gpointer data
)
2482 gboolean
*result
= (gboolean
*)data
;
2483 if (*result
== TRUE
)
2485 if (folder_has_parent_of_type(item
, F_TRASH
) && item
->total_msgs
> 0)
2489 gboolean
procmsg_have_trashed_mails_fast (void)
2491 gboolean result
= FALSE
;
2492 folder_func_to_all_folders(item_has_trashed_mails
, &result
);
2496 gchar
*procmsg_msginfo_get_tags_str(MsgInfo
*msginfo
)
2504 if (msginfo
->tags
== NULL
)
2506 for (cur
= msginfo
->tags
; cur
; cur
= cur
->next
) {
2507 const gchar
*tag
= tags_get_tag(GPOINTER_TO_INT(cur
->data
));
2511 tags
= g_strdup(tag
);
2513 int olen
= strlen(tags
);
2514 int nlen
= olen
+ strlen(tag
) + 2 /* strlen(", ") */;
2515 tags
= g_realloc(tags
, nlen
+1);
2518 strcpy(tags
+olen
, ", ");
2519 strcpy(tags
+olen
+2, tag
);
2526 void procmsg_msginfo_update_tags(MsgInfo
*msginfo
, gboolean set
, gint id
)
2534 msginfo
->tags
= g_slist_remove(
2536 GINT_TO_POINTER(id
));
2537 changed
.data
= GINT_TO_POINTER(id
);
2538 changed
.next
= NULL
;
2539 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, &changed
);
2541 if (!g_slist_find(msginfo
->tags
, GINT_TO_POINTER(id
))) {
2542 msginfo
->tags
= g_slist_append(
2544 GINT_TO_POINTER(id
));
2546 changed
.data
= GINT_TO_POINTER(id
);
2547 changed
.next
= NULL
;
2548 folder_item_commit_tags(msginfo
->folder
, msginfo
, &changed
, NULL
);
2553 void procmsg_msginfo_clear_tags(MsgInfo
*msginfo
)
2555 GSList
*unset
= msginfo
->tags
;
2556 msginfo
->tags
= NULL
;
2557 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, unset
);
2558 g_slist_free(unset
);