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/>.
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 "prefs_common.h"
41 #include "alertpanel.h"
45 #include "partial_download.h"
46 #include "mainwindow.h"
47 #include "summaryview.h"
53 #include "file-utils.h"
55 extern SessionStats session_stats
;
57 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
58 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
);
59 static void procmsg_update_unread_children (MsgInfo
*info
,
60 gboolean newly_marked
);
67 Q_MAIL_ACCOUNT_ID
= 4,
68 Q_NEWS_ACCOUNT_ID
= 5,
69 Q_SAVE_COPY_FOLDER
= 6,
70 Q_REPLY_MESSAGE_ID
= 7,
76 Q_PRIVACY_SYSTEM_OLD
= 13,
78 Q_ENCRYPT_DATA_OLD
= 15,
79 Q_CLAWS_HDRS_OLD
= 16,
82 void procmsg_msg_list_free(GSList
*mlist
)
87 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
88 msginfo
= (MsgInfo
*)cur
->data
;
89 procmsg_msginfo_free(&msginfo
);
94 MsgNumberList
*procmsg_get_number_list_for_msgs(MsgInfoList
*msglist
)
99 for (cur
= msglist
; cur
; cur
= cur
->next
) {
100 MsgInfo
*msg
= (MsgInfo
*)cur
->data
;
101 nums
= g_slist_prepend(nums
, GUINT_TO_POINTER(msg
->msgnum
));
104 return g_slist_reverse(nums
);
116 /* CLAWS subject threading:
118 in the first round it inserts subject lines in a
119 hashtable (subject <-> node)
121 the second round finishes the threads by attaching
122 matching subject lines to the one found in the
123 hashtable. will use the oldest node with the same
124 subject that is not more then thread_by_subject_max_age
125 days old (see subject_hashtable_lookup)
128 static void subject_hashtable_insert(GHashTable
*hashtable
, GNode
*node
)
134 cm_return_if_fail(hashtable
!= NULL
);
135 cm_return_if_fail(node
!= NULL
);
136 msginfo
= (MsgInfo
*) node
->data
;
137 cm_return_if_fail(msginfo
!= NULL
);
139 subject
= msginfo
->subject
;
143 subject
+= subject_get_prefix_length(subject
);
145 list
= g_hash_table_lookup(hashtable
, subject
);
146 list
= g_slist_prepend(list
, node
);
147 g_hash_table_insert(hashtable
, subject
, list
);
150 static GNode
*subject_hashtable_lookup(GHashTable
*hashtable
, MsgInfo
*msginfo
)
154 GNode
*node
= NULL
, *hashtable_node
= NULL
;
156 MsgInfo
*hashtable_msginfo
= NULL
, *best_msginfo
= NULL
;
159 cm_return_val_if_fail(hashtable
!= NULL
, NULL
);
161 subject
= msginfo
->subject
;
164 prefix_length
= subject_get_prefix_length(subject
);
165 if (prefix_length
<= 0)
167 subject
+= prefix_length
;
169 list
= g_hash_table_lookup(hashtable
, subject
);
173 /* check all nodes with the same subject to find the best parent */
174 for (cur
= list
; cur
; cur
= cur
->next
) {
175 hashtable_node
= (GNode
*)cur
->data
;
176 hashtable_msginfo
= (MsgInfo
*) hashtable_node
->data
;
179 /* best node should be the oldest in the found nodes */
180 /* parent node must not be older then msginfo */
181 if ((hashtable_msginfo
->date_t
< msginfo
->date_t
) &&
182 ((best_msginfo
== NULL
) ||
183 (best_msginfo
->date_t
> hashtable_msginfo
->date_t
)))
186 /* parent node must not be more then thread_by_subject_max_age
187 days older then msginfo */
188 if (fabs(difftime(msginfo
->date_t
, hashtable_msginfo
->date_t
)) >
189 prefs_common
.thread_by_subject_max_age
* 3600 * 24)
192 /* can add new tests for all matching
193 nodes found by subject */
196 node
= hashtable_node
;
197 best_msginfo
= hashtable_msginfo
;
204 static void subject_hashtable_free(gpointer key
, gpointer value
, gpointer data
)
209 /* return the reversed thread tree */
210 GNode
*procmsg_get_thread_tree(GSList
*mlist
)
212 GNode
*root
, *parent
, *node
, *next
;
213 GHashTable
*msgid_table
;
214 GHashTable
*subject_hashtable
= NULL
;
219 root
= g_node_new(NULL
);
220 msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
222 if (prefs_common
.thread_by_subject
) {
223 subject_hashtable
= g_hash_table_new(g_str_hash
, g_str_equal
);
226 for (; mlist
!= NULL
; mlist
= mlist
->next
) {
227 msginfo
= (MsgInfo
*)mlist
->data
;
230 if (msginfo
->inreplyto
) {
231 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
232 if (parent
== NULL
) {
236 node
= g_node_insert_data_before
237 (parent
, parent
== root
? parent
->children
: NULL
,
239 if ((msgid
= msginfo
->msgid
) && g_hash_table_lookup(msgid_table
, msgid
) == NULL
)
240 g_hash_table_insert(msgid_table
, (gchar
*)msgid
, node
);
242 /* CLAWS: add subject to hashtable (without prefix) */
243 if (prefs_common
.thread_by_subject
) {
244 subject_hashtable_insert(subject_hashtable
, node
);
248 /* complete the unfinished threads */
249 for (node
= root
->children
; node
!= NULL
; ) {
251 msginfo
= (MsgInfo
*)node
->data
;
254 if (msginfo
->inreplyto
)
255 parent
= g_hash_table_lookup(msgid_table
, msginfo
->inreplyto
);
257 /* try looking for the indirect parent */
258 if (!parent
&& msginfo
->references
) {
259 for (reflist
= msginfo
->references
;
260 reflist
!= NULL
; reflist
= reflist
->next
)
261 if ((parent
= g_hash_table_lookup
262 (msgid_table
, reflist
->data
)) != NULL
)
266 /* node should not be the parent, and node should not
267 be an ancestor of parent (circular reference) */
268 if (parent
&& parent
!= node
&&
269 !g_node_is_ancestor(node
, parent
)) {
272 (parent
, parent
->children
, node
);
278 if (prefs_common
.thread_by_subject
) {
279 START_TIMING("thread by subject");
280 for (node
= root
->children
; node
&& node
!= NULL
;) {
282 msginfo
= (MsgInfo
*) node
->data
;
284 parent
= subject_hashtable_lookup(subject_hashtable
, msginfo
);
286 /* the node may already be threaded by IN-REPLY-TO, so go up
288 find the parent node */
289 if (parent
!= NULL
) {
290 if (g_node_is_ancestor(node
, parent
))
298 g_node_append(parent
, node
);
306 if (prefs_common
.thread_by_subject
)
308 g_hash_table_foreach(subject_hashtable
, subject_hashtable_free
, NULL
);
309 g_hash_table_destroy(subject_hashtable
);
312 g_hash_table_destroy(msgid_table
);
317 gint
procmsg_move_messages(GSList
*mlist
)
319 GSList
*cur
, *movelist
= NULL
;
321 FolderItem
*dest
= NULL
;
323 gboolean finished
= TRUE
;
324 if (!mlist
) return 0;
326 folder_item_update_freeze();
329 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
330 msginfo
= (MsgInfo
*)cur
->data
;
331 if (!msginfo
->to_folder
) {
337 dest
= msginfo
->to_folder
;
338 movelist
= g_slist_prepend(movelist
, msginfo
);
339 } else if (dest
== msginfo
->to_folder
) {
340 movelist
= g_slist_prepend(movelist
, msginfo
);
344 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
347 movelist
= g_slist_reverse(movelist
);
348 retval
|= folder_item_move_msgs(dest
, movelist
);
349 g_slist_free(movelist
);
352 if (finished
== FALSE
) {
358 folder_item_update_thaw();
362 void procmsg_copy_messages(GSList
*mlist
)
364 GSList
*cur
, *copylist
= NULL
;
366 FolderItem
*dest
= NULL
;
367 gboolean finished
= TRUE
;
370 folder_item_update_freeze();
373 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
374 msginfo
= (MsgInfo
*)cur
->data
;
375 if (!msginfo
->to_folder
) {
381 dest
= msginfo
->to_folder
;
382 copylist
= g_slist_prepend(copylist
, msginfo
);
383 } else if (dest
== msginfo
->to_folder
) {
384 copylist
= g_slist_prepend(copylist
, msginfo
);
388 procmsg_msginfo_set_to_folder(msginfo
, NULL
);
391 copylist
= g_slist_reverse(copylist
);
392 folder_item_copy_msgs(dest
, copylist
);
393 g_slist_free(copylist
);
396 if (finished
== FALSE
) {
402 folder_item_update_thaw();
405 gchar
*procmsg_get_message_file_path(MsgInfo
*msginfo
)
407 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
409 return folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
412 gchar
*procmsg_get_message_file(MsgInfo
*msginfo
)
414 gchar
*filename
= NULL
;
416 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
418 filename
= folder_item_fetch_msg(msginfo
->folder
, msginfo
->msgnum
);
420 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
425 gchar
*procmsg_get_message_file_full(MsgInfo
*msginfo
, gboolean headers
, gboolean body
)
427 gchar
*filename
= NULL
;
429 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
431 filename
= folder_item_fetch_msg_full(msginfo
->folder
, msginfo
->msgnum
,
434 debug_print("can't fetch message %d\n", msginfo
->msgnum
);
439 GSList
*procmsg_get_message_file_list(GSList
*mlist
)
441 GSList
*file_list
= NULL
;
443 MsgFileInfo
*fileinfo
;
446 while (mlist
!= NULL
) {
447 msginfo
= (MsgInfo
*)mlist
->data
;
448 file
= procmsg_get_message_file(msginfo
);
450 procmsg_message_file_list_free(file_list
);
453 fileinfo
= g_new(MsgFileInfo
, 1);
454 fileinfo
->msginfo
= procmsg_msginfo_new_ref(msginfo
);
455 fileinfo
->file
= file
;
456 fileinfo
->flags
= g_new(MsgFlags
, 1);
457 *fileinfo
->flags
= msginfo
->flags
;
458 file_list
= g_slist_prepend(file_list
, fileinfo
);
462 file_list
= g_slist_reverse(file_list
);
467 void procmsg_message_file_list_free(MsgInfoList
*file_list
)
470 MsgFileInfo
*fileinfo
;
472 for (cur
= file_list
; cur
!= NULL
; cur
= cur
->next
) {
473 fileinfo
= (MsgFileInfo
*)cur
->data
;
474 procmsg_msginfo_free(&(fileinfo
->msginfo
));
475 g_free(fileinfo
->file
);
476 g_free(fileinfo
->flags
);
480 g_slist_free(file_list
);
483 FILE *procmsg_open_message(MsgInfo
*msginfo
, gboolean skip_special_headers
)
488 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
490 file
= procmsg_get_message_file_path(msginfo
);
491 cm_return_val_if_fail(file
!= NULL
, NULL
);
493 if (!is_file_exist(file
)) {
495 file
= procmsg_get_message_file(msginfo
);
500 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
501 FILE_OP_ERROR(file
, "claws_fopen");
508 if (MSG_IS_QUEUED(msginfo
->flags
) || MSG_IS_DRAFT(msginfo
->flags
) ||
509 skip_special_headers
== TRUE
) {
512 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
514 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
515 strlen("X-Claws-End-Special-Headers:"))) ||
516 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
517 strlen("X-Sylpheed-End-Special-Headers:"))))
520 if (buf
[0] == '\r' || buf
[0] == '\n') break;
521 /* from other mailers */
522 if (!strncmp(buf
, "Date: ", 6)
523 || !strncmp(buf
, "To: ", 4)
524 || !strncmp(buf
, "From: ", 6)
525 || !strncmp(buf
, "Subject: ", 9)) {
535 gboolean
procmsg_msg_exist(MsgInfo
*msginfo
)
539 if (!msginfo
) return FALSE
;
541 ret
= !folder_item_is_msg_changed(msginfo
->folder
, msginfo
);
546 void procmsg_get_filter_keyword(MsgInfo
*msginfo
, gchar
**header
, gchar
**key
,
547 PrefsFilterType type
)
549 static HeaderEntry hentry
[] = {{"X-BeenThere:", NULL
, TRUE
},
550 {"X-ML-Name:", NULL
, TRUE
},
551 {"X-List:", NULL
, TRUE
},
552 {"X-Mailing-list:", NULL
, TRUE
},
553 {"List-Id:", NULL
, TRUE
},
554 {"X-Sequence:", NULL
, TRUE
},
555 {"Sender:", NULL
, TRUE
},
556 {"List-Post:", NULL
, TRUE
},
557 {NULL
, NULL
, FALSE
}};
563 H_X_MAILING_LIST
= 3,
572 cm_return_if_fail(msginfo
!= NULL
);
573 cm_return_if_fail(header
!= NULL
);
574 cm_return_if_fail(key
!= NULL
);
583 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
)
585 procheader_get_header_fields(fp
, hentry
);
588 #define SET_FILTER_KEY(hstr, idx) \
590 *header = g_strdup(hstr); \
591 *key = hentry[idx].body; \
592 hentry[idx].body = NULL; \
595 if (hentry
[H_LIST_ID
].body
!= NULL
) {
596 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID
);
597 extract_list_id_str(*key
);
598 } else if (hentry
[H_X_BEENTHERE
].body
!= NULL
) {
599 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE
);
600 } else if (hentry
[H_X_ML_NAME
].body
!= NULL
) {
601 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME
);
602 } else if (hentry
[H_X_LIST
].body
!= NULL
) {
603 SET_FILTER_KEY("header \"X-List\"", H_X_LIST
);
604 } else if (hentry
[H_X_MAILING_LIST
].body
!= NULL
) {
605 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST
);
606 } else if (hentry
[H_X_SEQUENCE
].body
!= NULL
) {
609 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE
);
612 while (*p
!= '\0' && !g_ascii_isspace(*p
)) p
++;
613 while (g_ascii_isspace(*p
)) p
++;
614 if (g_ascii_isdigit(*p
)) {
620 } else if (hentry
[H_SENDER
].body
!= NULL
) {
621 SET_FILTER_KEY("header \"Sender\"", H_SENDER
);
622 } else if (hentry
[H_LIST_POST
].body
!= NULL
) {
623 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST
);
624 } else if (msginfo
->to
) {
625 *header
= g_strdup("to");
626 *key
= g_strdup(msginfo
->to
);
627 } else if (msginfo
->subject
) {
628 *header
= g_strdup("subject");
629 *key
= g_strdup(msginfo
->subject
);
632 g_free(hentry
[H_X_BEENTHERE
].body
);
633 hentry
[H_X_BEENTHERE
].body
= NULL
;
634 g_free(hentry
[H_X_ML_NAME
].body
);
635 hentry
[H_X_ML_NAME
].body
= NULL
;
636 g_free(hentry
[H_X_LIST
].body
);
637 hentry
[H_X_LIST
].body
= NULL
;
638 g_free(hentry
[H_X_MAILING_LIST
].body
);
639 hentry
[H_X_MAILING_LIST
].body
= NULL
;
640 g_free(hentry
[H_LIST_ID
].body
);
641 hentry
[H_LIST_ID
].body
= NULL
;
642 g_free(hentry
[H_SENDER
].body
);
643 hentry
[H_SENDER
].body
= NULL
;
644 g_free(hentry
[H_LIST_POST
].body
);
645 hentry
[H_LIST_POST
].body
= NULL
;
649 *header
= g_strdup("from");
650 *key
= g_strdup(msginfo
->from
);
653 *header
= g_strdup("to");
654 *key
= g_strdup(msginfo
->to
);
656 case FILTER_BY_SUBJECT
:
657 *header
= g_strdup("subject");
658 *key
= g_strdup(msginfo
->subject
);
660 case FILTER_BY_SENDER
:
661 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
)
663 procheader_get_header_fields(fp
, hentry
);
666 if (hentry
[H_SENDER
].body
!= NULL
)
667 SET_FILTER_KEY("header \"Sender\"", H_SENDER
);
669 g_free(hentry
[H_X_BEENTHERE
].body
);
670 hentry
[H_X_BEENTHERE
].body
= NULL
;
671 g_free(hentry
[H_X_ML_NAME
].body
);
672 hentry
[H_X_ML_NAME
].body
= NULL
;
673 g_free(hentry
[H_X_LIST
].body
);
674 hentry
[H_X_LIST
].body
= NULL
;
675 g_free(hentry
[H_X_MAILING_LIST
].body
);
676 hentry
[H_X_MAILING_LIST
].body
= NULL
;
677 g_free(hentry
[H_LIST_ID
].body
);
678 hentry
[H_LIST_ID
].body
= NULL
;
679 g_free(hentry
[H_SENDER
].body
);
680 hentry
[H_SENDER
].body
= NULL
;
682 #undef SET_FILTER_KEY
689 static void procmsg_empty_trash(FolderItem
*trash
)
694 (trash
->stype
!= F_TRASH
&&
695 !folder_has_parent_of_type(trash
, F_TRASH
)))
698 if (trash
&& trash
->total_msgs
> 0) {
699 GSList
*mlist
= folder_item_get_msg_list(trash
);
701 for (cur
= mlist
; cur
!= NULL
; cur
= cur
->next
) {
702 MsgInfo
* msginfo
= (MsgInfo
*) cur
->data
;
703 if (MSG_IS_LOCKED(msginfo
->flags
)) {
704 procmsg_msginfo_free(&msginfo
);
707 if (msginfo
->total_size
!= 0 &&
708 msginfo
->size
!= (off_t
)msginfo
->total_size
)
709 partial_mark_for_delete(msginfo
);
711 procmsg_msginfo_free(&msginfo
);
714 folder_item_remove_all_msg(trash
);
717 if (!trash
->node
|| !trash
->node
->children
)
720 node
= trash
->node
->children
;
721 while (node
!= NULL
) {
723 procmsg_empty_trash(FOLDER_ITEM(node
->data
));
728 void procmsg_empty_all_trash(void)
733 for (cur
= folder_get_list(); cur
!= NULL
; cur
= cur
->next
) {
734 Folder
*folder
= FOLDER(cur
->data
);
735 trash
= folder
->trash
;
736 procmsg_empty_trash(trash
);
737 if (folder
->account
&& folder
->account
->set_trash_folder
&&
738 folder_find_item_from_identifier(folder
->account
->trash_folder
))
740 folder_find_item_from_identifier(folder
->account
->trash_folder
));
744 static PrefsAccount
*procmsg_get_account_from_file(const gchar
*file
)
746 PrefsAccount
*mailac
= NULL
;
750 static HeaderEntry qentry
[] = {{"S:", NULL
, FALSE
},
751 {"SSV:", NULL
, FALSE
},
753 {"NG:", NULL
, FALSE
},
754 {"MAID:", NULL
, FALSE
},
755 {"NAID:", NULL
, FALSE
},
756 {"SCF:", NULL
, FALSE
},
757 {"RMID:", NULL
, FALSE
},
758 {"FMID:", NULL
, FALSE
},
759 {"X-Claws-Privacy-System:", NULL
, FALSE
},
760 {"X-Claws-Encrypt:", NULL
, FALSE
},
761 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
762 {"X-Claws-End-Special-Headers", NULL
, FALSE
},
763 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
764 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
765 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
},
766 {NULL
, NULL
, FALSE
}};
768 cm_return_val_if_fail(file
!= NULL
, NULL
);
770 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
771 FILE_OP_ERROR(file
, "claws_fopen");
775 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
776 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
778 if (hnum
== Q_MAIL_ACCOUNT_ID
) {
779 mailac
= account_find_from_id(atoi(p
));
791 gchar
*procmsg_msginfo_get_avatar(MsgInfo
*msginfo
, gint type
)
795 if (!msginfo
|| !msginfo
->extradata
|| !msginfo
->extradata
->avatars
)
798 for (mia
= msginfo
->extradata
->avatars
; mia
; mia
= mia
->next
) {
799 MsgInfoAvatar
*avatar
= (MsgInfoAvatar
*)mia
->data
;
800 if (avatar
->avatar_id
== type
)
801 return avatar
->avatar_src
;
807 void procmsg_msginfo_add_avatar(MsgInfo
*msginfo
, gint type
, const gchar
*data
)
811 if (!msginfo
->extradata
)
812 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
814 av
= g_new0(MsgInfoAvatar
, 1);
815 av
->avatar_id
= type
;
816 av
->avatar_src
= g_strdup(data
);
818 msginfo
->extradata
->avatars
= g_slist_append(msginfo
->extradata
->avatars
, av
);
821 gchar
*procmsg_msginfo_get_identifier(MsgInfo
*msginfo
)
827 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
828 folder_id
= folder_item_get_identifier(msginfo
->folder
);
829 msgid
= msginfo
->msgid
;
831 id
= g_strconcat(folder_id
, G_DIR_SEPARATOR_S
, msgid
, NULL
);
838 MsgInfo
*procmsg_get_msginfo_from_identifier(const gchar
*id
)
840 gchar
*folder_id
= g_strdup(id
);
841 gchar
*separator
= strrchr(folder_id
, G_DIR_SEPARATOR
);
846 if (separator
== NULL
) {
852 msgid
= separator
+ 1;
854 item
= folder_find_item_from_identifier(folder_id
);
861 msginfo
= folder_item_get_msginfo_by_msgid(item
, msgid
);
867 static GSList
*procmsg_list_sort_by_account(FolderItem
*queue
, GSList
*list
)
869 GSList
*result
= NULL
;
871 PrefsAccount
*last_account
= NULL
;
874 gboolean nothing_to_sort
= TRUE
;
879 orig
= g_slist_copy(list
);
881 msg
= (MsgInfo
*)orig
->data
;
883 for (cur
= orig
; cur
; cur
= cur
->next
)
884 debug_print("sort before %s\n", ((MsgInfo
*)cur
->data
)->from
);
889 nothing_to_sort
= TRUE
;
893 PrefsAccount
*ac
= NULL
;
894 msg
= (MsgInfo
*)cur
->data
;
895 file
= folder_item_fetch_msg(queue
, msg
->msgnum
);
896 ac
= procmsg_get_account_from_file(file
);
899 if (last_account
== NULL
|| (ac
!= NULL
&& ac
== last_account
)) {
900 result
= g_slist_append(result
, msg
);
901 orig
= g_slist_remove(orig
, msg
);
903 nothing_to_sort
= FALSE
;
909 if (orig
&& g_slist_length(orig
)) {
910 if (!last_account
&& nothing_to_sort
) {
911 /* can't find an account for the rest of the list */
914 result
= g_slist_append(result
, cur
->data
);
925 for (cur
= result
; cur
; cur
= cur
->next
)
926 debug_print("sort after %s\n", ((MsgInfo
*)cur
->data
)->from
);
933 static gboolean
procmsg_is_last_for_account(FolderItem
*queue
, MsgInfo
*msginfo
, GSList
*elem
)
935 gchar
*file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
936 PrefsAccount
*ac
= procmsg_get_account_from_file(file
);
939 for (cur
= elem
; cur
; cur
= cur
->next
) {
940 MsgInfo
*cur_msginfo
= (MsgInfo
*)cur
->data
;
941 file
= folder_item_fetch_msg(queue
, cur_msginfo
->msgnum
);
943 if (cur_msginfo
!= msginfo
&& !MSG_IS_LOCKED(cur_msginfo
->flags
)) {
944 if (procmsg_get_account_from_file(file
) == ac
) {
955 static gboolean send_queue_lock
= FALSE
;
957 gboolean
procmsg_queue_lock(char **errstr
)
959 if (send_queue_lock
) {
960 /* Avoid having to translate two similar strings */
961 log_warning(LOG_PROTOCOL
, "%s\n", _("Already trying to send."));
963 if (*errstr
) g_free(*errstr
);
964 *errstr
= g_strdup_printf(_("Already trying to send."));
968 send_queue_lock
= TRUE
;
971 void procmsg_queue_unlock(void)
973 send_queue_lock
= FALSE
;
976 *\brief Send messages in queue
978 *\param queue Queue folder to process
979 *\param save_msgs Unused
981 *\return Number of messages sent, negative if an error occurred
982 * positive if no error occurred
984 gint
procmsg_send_queue(FolderItem
*queue
, gboolean save_msgs
, gchar
**errstr
)
986 gint sent
= 0, err
= 0;
988 GSList
*sorted_list
= NULL
;
991 if (!procmsg_queue_lock(errstr
)) {
992 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
993 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
998 queue
= folder_get_default_queue();
1000 if (queue
== NULL
) {
1001 procmsg_queue_unlock();
1006 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1007 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1009 folder_item_scan(queue
);
1010 list
= folder_item_get_msg_list(queue
);
1012 /* sort the list per sender account; this helps reusing the same SMTP server */
1013 sorted_list
= procmsg_list_sort_by_account(queue
, list
);
1015 for (elem
= sorted_list
; elem
!= NULL
; elem
= elem
->next
) {
1019 msginfo
= (MsgInfo
*)(elem
->data
);
1020 if (!MSG_IS_LOCKED(msginfo
->flags
) && !MSG_IS_DELETED(msginfo
->flags
)) {
1021 file
= folder_item_fetch_msg(queue
, msginfo
->msgnum
);
1023 gboolean queued_removed
= FALSE
;
1024 if (procmsg_send_message_queue_full(file
,
1025 !procmsg_is_last_for_account(queue
, msginfo
, elem
),
1026 errstr
, queue
, msginfo
->msgnum
, &queued_removed
) < 0) {
1027 g_warning("sending queued message %d failed",
1032 if (!queued_removed
)
1033 folder_item_remove_msg(queue
, msginfo
->msgnum
);
1038 /* FIXME: supposedly if only one message is locked, and queue
1039 * is being flushed, the following free says something like
1040 * "freeing msg ## in folder (nil)". */
1041 procmsg_msginfo_free(&msginfo
);
1044 g_slist_free(sorted_list
);
1045 folder_item_scan(queue
);
1047 if (queue
->node
&& queue
->node
->children
) {
1048 node
= queue
->node
->children
;
1049 while (node
!= NULL
) {
1052 send_queue_lock
= FALSE
;
1053 res
= procmsg_send_queue(FOLDER_ITEM(node
->data
), save_msgs
, errstr
);
1054 send_queue_lock
= TRUE
;
1062 procmsg_queue_unlock();
1064 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1065 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1067 return (err
!= 0 ? -err
: sent
);
1070 gboolean
procmsg_is_sending(void)
1072 return send_queue_lock
;
1076 *\brief Determine if a queue folder is empty
1078 *\param queue Queue folder to process
1080 *\return TRUE if the queue folder is empty, otherwise return FALSE
1082 gboolean
procmsg_queue_is_empty(FolderItem
*queue
)
1085 gboolean res
= FALSE
;
1087 queue
= folder_get_default_queue();
1088 cm_return_val_if_fail(queue
!= NULL
, TRUE
);
1090 folder_item_scan(queue
);
1091 list
= folder_item_get_msg_list(queue
);
1092 res
= (list
== NULL
);
1093 procmsg_msg_list_free(list
);
1097 if (queue
->node
&& queue
->node
->children
) {
1098 node
= queue
->node
->children
;
1099 while (node
!= NULL
) {
1101 if (!procmsg_queue_is_empty(FOLDER_ITEM(node
->data
)))
1110 gint
procmsg_remove_special_headers(const gchar
*in
, const gchar
*out
)
1113 gchar buf
[BUFFSIZE
];
1115 if ((fp
= claws_fopen(in
, "rb")) == NULL
) {
1116 FILE_OP_ERROR(in
, "claws_fopen");
1119 if ((outfp
= claws_fopen(out
, "wb")) == NULL
) {
1120 FILE_OP_ERROR(out
, "claws_fopen");
1124 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1126 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
1127 strlen("X-Claws-End-Special-Headers:"))) ||
1128 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
1129 strlen("X-Sylpheed-End-Special-Headers:"))))
1132 if (buf
[0] == '\r' || buf
[0] == '\n') break;
1133 /* from other mailers */
1134 if (!strncmp(buf
, "Date: ", 6)
1135 || !strncmp(buf
, "To: ", 4)
1136 || !strncmp(buf
, "From: ", 6)
1137 || !strncmp(buf
, "Subject: ", 9)) {
1142 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1143 if (claws_fputs(buf
, outfp
) == EOF
) {
1144 FILE_OP_ERROR(out
, "claws_fputs");
1145 claws_fclose(outfp
);
1150 claws_safe_fclose(outfp
);
1155 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("saving sent message to %s...\n", outbox_path
);
1172 g_free(outbox_path
);
1174 /* remove queueing headers */
1176 gchar tmp
[MAXPATHLEN
+ 1];
1178 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg.out.%08x",
1179 get_rc_dir(), G_DIR_SEPARATOR
, (guint
) rand());
1181 if (procmsg_remove_special_headers(file
, tmp
) !=0)
1184 folder_item_scan(outbox
);
1185 if ((num
= folder_item_add_msg(outbox
, tmp
, &flag
, TRUE
)) < 0) {
1186 g_warning("can't save message");
1191 folder_item_scan(outbox
);
1192 if ((num
= folder_item_add_msg
1193 (outbox
, file
, &flag
, FALSE
)) < 0) {
1194 g_warning("can't save message");
1198 msginfo
= folder_item_get_msginfo(outbox
, num
); /* refcnt++ */
1199 tmp_msginfo
= procmsg_msginfo_get_full_info(msginfo
); /* refcnt++ */
1200 if (msginfo
!= NULL
) {
1201 procmsg_msginfo_unset_flags(msginfo
, ~0, 0);
1202 procmsg_msginfo_free(&msginfo
); /* refcnt-- */
1203 /* tmp_msginfo == msginfo */
1204 if (tmp_msginfo
&& msginfo
->extradata
&&
1205 (msginfo
->extradata
->dispositionnotificationto
||
1206 msginfo
->extradata
->returnreceiptto
)) {
1207 procmsg_msginfo_set_flags(msginfo
, MSG_RETRCPT_SENT
, 0);
1209 procmsg_msginfo_free(&tmp_msginfo
); /* refcnt-- */
1216 MsgInfo
*procmsg_msginfo_new_ref(MsgInfo
*msginfo
)
1223 MsgInfo
*procmsg_msginfo_new(void)
1225 MsgInfo
*newmsginfo
;
1227 newmsginfo
= g_new0(MsgInfo
, 1);
1228 newmsginfo
->refcnt
= 1;
1233 static MsgInfoAvatar
*procmsg_msginfoavatar_copy(MsgInfoAvatar
*avatar
)
1235 MsgInfoAvatar
*newavatar
;
1237 if (avatar
== NULL
) return NULL
;
1239 newavatar
= g_new0(MsgInfoAvatar
, 1);
1240 newavatar
->avatar_id
= avatar
->avatar_id
;
1241 newavatar
->avatar_src
= g_strdup(avatar
->avatar_src
);
1246 static void procmsg_msginfoavatar_free(MsgInfoAvatar
*avatar
)
1248 if (avatar
!= NULL
) {
1249 if (avatar
->avatar_src
!= NULL
)
1250 g_free(avatar
->avatar_src
);
1255 MsgInfo
*procmsg_msginfo_copy(MsgInfo
*msginfo
)
1257 MsgInfo
*newmsginfo
;
1260 if (msginfo
== NULL
) return NULL
;
1262 newmsginfo
= g_new0(MsgInfo
, 1);
1264 newmsginfo
->refcnt
= 1;
1266 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1267 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1268 g_strdup(msginfo->mmb) : NULL
1283 MEMBDUP(newsgroups
);
1290 MEMBCOPY(to_folder
);
1292 if (msginfo
->extradata
) {
1293 newmsginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1294 if (msginfo
->extradata
->avatars
) {
1295 newmsginfo
->extradata
->avatars
= g_slist_copy_deep(msginfo
->extradata
->avatars
,
1296 (GCopyFunc
) procmsg_msginfoavatar_copy
, NULL
);
1298 MEMBDUP(extradata
->dispositionnotificationto
);
1299 MEMBDUP(extradata
->returnreceiptto
);
1300 MEMBDUP(extradata
->partial_recv
);
1301 MEMBDUP(extradata
->account_server
);
1302 MEMBDUP(extradata
->account_login
);
1303 MEMBDUP(extradata
->list_post
);
1304 MEMBDUP(extradata
->list_subscribe
);
1305 MEMBDUP(extradata
->list_unsubscribe
);
1306 MEMBDUP(extradata
->list_help
);
1307 MEMBDUP(extradata
->list_archive
);
1308 MEMBDUP(extradata
->list_owner
);
1309 MEMBDUP(extradata
->resent_from
);
1312 refs
= msginfo
->references
;
1313 for (refs
= msginfo
->references
; refs
!= NULL
; refs
= refs
->next
) {
1314 newmsginfo
->references
= g_slist_prepend
1315 (newmsginfo
->references
, g_strdup(refs
->data
));
1317 newmsginfo
->references
= g_slist_reverse(newmsginfo
->references
);
1320 MEMBDUP(plaintext_file
);
1325 MsgInfo
*procmsg_msginfo_get_full_info_from_file(MsgInfo
*msginfo
, const gchar
*file
)
1327 MsgInfo
*full_msginfo
;
1329 if (msginfo
== NULL
) return NULL
;
1331 if (!file
|| !is_file_exist(file
)) {
1332 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file");
1336 full_msginfo
= procheader_parse_file(file
, msginfo
->flags
, TRUE
, FALSE
);
1337 if (!full_msginfo
) return NULL
;
1339 msginfo
->total_size
= full_msginfo
->total_size
;
1340 msginfo
->planned_download
= full_msginfo
->planned_download
;
1342 if (full_msginfo
->extradata
) {
1343 if (!msginfo
->extradata
)
1344 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
1345 if (!msginfo
->extradata
->list_post
)
1346 msginfo
->extradata
->list_post
= g_strdup(full_msginfo
->extradata
->list_post
);
1347 if (!msginfo
->extradata
->list_subscribe
)
1348 msginfo
->extradata
->list_subscribe
= g_strdup(full_msginfo
->extradata
->list_subscribe
);
1349 if (!msginfo
->extradata
->list_unsubscribe
)
1350 msginfo
->extradata
->list_unsubscribe
= g_strdup(full_msginfo
->extradata
->list_unsubscribe
);
1351 if (!msginfo
->extradata
->list_help
)
1352 msginfo
->extradata
->list_help
= g_strdup(full_msginfo
->extradata
->list_help
);
1353 if (!msginfo
->extradata
->list_archive
)
1354 msginfo
->extradata
->list_archive
= g_strdup(full_msginfo
->extradata
->list_archive
);
1355 if (!msginfo
->extradata
->list_owner
)
1356 msginfo
->extradata
->list_owner
= g_strdup(full_msginfo
->extradata
->list_owner
);
1357 if (!msginfo
->extradata
->avatars
)
1358 msginfo
->extradata
->avatars
= g_slist_copy_deep(full_msginfo
->extradata
->avatars
,
1359 (GCopyFunc
) procmsg_msginfoavatar_copy
, NULL
);
1360 if (!msginfo
->extradata
->dispositionnotificationto
)
1361 msginfo
->extradata
->dispositionnotificationto
=
1362 g_strdup(full_msginfo
->extradata
->dispositionnotificationto
);
1363 if (!msginfo
->extradata
->returnreceiptto
)
1364 msginfo
->extradata
->returnreceiptto
= g_strdup
1365 (full_msginfo
->extradata
->returnreceiptto
);
1366 if (!msginfo
->extradata
->partial_recv
&& full_msginfo
->extradata
->partial_recv
)
1367 msginfo
->extradata
->partial_recv
= g_strdup
1368 (full_msginfo
->extradata
->partial_recv
);
1369 if (!msginfo
->extradata
->account_server
&& full_msginfo
->extradata
->account_server
)
1370 msginfo
->extradata
->account_server
= g_strdup
1371 (full_msginfo
->extradata
->account_server
);
1372 if (!msginfo
->extradata
->account_login
&& full_msginfo
->extradata
->account_login
)
1373 msginfo
->extradata
->account_login
= g_strdup
1374 (full_msginfo
->extradata
->account_login
);
1375 if (!msginfo
->extradata
->resent_from
&& full_msginfo
->extradata
->resent_from
)
1376 msginfo
->extradata
->resent_from
= g_strdup
1377 (full_msginfo
->extradata
->resent_from
);
1379 procmsg_msginfo_free(&full_msginfo
);
1381 return procmsg_msginfo_new_ref(msginfo
);
1384 MsgInfo
*procmsg_msginfo_get_full_info(MsgInfo
*msginfo
)
1386 MsgInfo
*full_msginfo
;
1389 if (msginfo
== NULL
) return NULL
;
1391 file
= procmsg_get_message_file_path(msginfo
);
1392 if (!file
|| !is_file_exist(file
)) {
1394 file
= procmsg_get_message_file(msginfo
);
1396 if (!file
|| !is_file_exist(file
)) {
1397 g_warning("procmsg_msginfo_get_full_info(): can't get message file");
1401 full_msginfo
= procmsg_msginfo_get_full_info_from_file(msginfo
, file
);
1403 return full_msginfo
;
1406 #define FREENULL(n) { g_free(n); n = NULL; }
1407 void procmsg_msginfo_free(MsgInfo
**msginfo_ptr
)
1409 MsgInfo
*msginfo
= *msginfo_ptr
;
1411 if (msginfo
== NULL
) return;
1414 if (msginfo
->refcnt
> 0)
1417 if (msginfo
->to_folder
) {
1418 msginfo
->to_folder
->op_count
--;
1419 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
1422 FREENULL(msginfo
->fromspace
);
1424 FREENULL(msginfo
->fromname
);
1426 FREENULL(msginfo
->date
);
1427 FREENULL(msginfo
->from
);
1428 FREENULL(msginfo
->to
);
1429 FREENULL(msginfo
->cc
);
1430 FREENULL(msginfo
->newsgroups
);
1431 FREENULL(msginfo
->subject
);
1432 FREENULL(msginfo
->msgid
);
1433 FREENULL(msginfo
->inreplyto
);
1434 FREENULL(msginfo
->xref
);
1436 if (msginfo
->extradata
) {
1437 if (msginfo
->extradata
->avatars
) {
1438 g_slist_foreach(msginfo
->extradata
->avatars
,
1439 (GFunc
)procmsg_msginfoavatar_free
,
1441 g_slist_free(msginfo
->extradata
->avatars
);
1442 msginfo
->extradata
->avatars
= NULL
;
1444 FREENULL(msginfo
->extradata
->returnreceiptto
);
1445 FREENULL(msginfo
->extradata
->dispositionnotificationto
);
1446 FREENULL(msginfo
->extradata
->list_post
);
1447 FREENULL(msginfo
->extradata
->list_subscribe
);
1448 FREENULL(msginfo
->extradata
->list_unsubscribe
);
1449 FREENULL(msginfo
->extradata
->list_help
);
1450 FREENULL(msginfo
->extradata
->list_archive
);
1451 FREENULL(msginfo
->extradata
->list_owner
);
1452 FREENULL(msginfo
->extradata
->partial_recv
);
1453 FREENULL(msginfo
->extradata
->account_server
);
1454 FREENULL(msginfo
->extradata
->account_login
);
1455 FREENULL(msginfo
->extradata
->resent_from
);
1456 FREENULL(msginfo
->extradata
);
1458 slist_free_strings_full(msginfo
->references
);
1459 msginfo
->references
= NULL
;
1460 g_slist_free(msginfo
->tags
);
1461 msginfo
->tags
= NULL
;
1463 FREENULL(msginfo
->plaintext_file
);
1466 *msginfo_ptr
= NULL
;
1470 guint
procmsg_msginfo_memusage(MsgInfo
*msginfo
)
1475 memusage
+= sizeof(MsgInfo
);
1476 if (msginfo
->fromname
)
1477 memusage
+= strlen(msginfo
->fromname
);
1479 memusage
+= strlen(msginfo
->date
);
1481 memusage
+= strlen(msginfo
->from
);
1483 memusage
+= strlen(msginfo
->to
);
1485 memusage
+= strlen(msginfo
->cc
);
1486 if (msginfo
->newsgroups
)
1487 memusage
+= strlen(msginfo
->newsgroups
);
1488 if (msginfo
->subject
)
1489 memusage
+= strlen(msginfo
->subject
);
1491 memusage
+= strlen(msginfo
->msgid
);
1492 if (msginfo
->inreplyto
)
1493 memusage
+= strlen(msginfo
->inreplyto
);
1495 for (tmp
= msginfo
->references
; tmp
; tmp
=tmp
->next
) {
1496 gchar
*r
= (gchar
*)tmp
->data
;
1497 memusage
+= r
?strlen(r
):0 + sizeof(GSList
);
1499 if (msginfo
->fromspace
)
1500 memusage
+= strlen(msginfo
->fromspace
);
1502 for (tmp
= msginfo
->tags
; tmp
; tmp
=tmp
->next
) {
1503 memusage
+= sizeof(GSList
);
1505 if (msginfo
->extradata
) {
1506 memusage
+= sizeof(MsgInfoExtraData
);
1507 if (msginfo
->extradata
->avatars
) {
1508 for (tmp
= msginfo
->extradata
->avatars
; tmp
; tmp
= tmp
->next
) {
1509 MsgInfoAvatar
*avt
= (MsgInfoAvatar
*)tmp
->data
;
1510 memusage
+= (avt
->avatar_src
)? strlen(avt
->avatar_src
): 0;
1511 memusage
+= sizeof(MsgInfoAvatar
) + sizeof(GSList
);
1514 if (msginfo
->extradata
->dispositionnotificationto
)
1515 memusage
+= strlen(msginfo
->extradata
->dispositionnotificationto
);
1516 if (msginfo
->extradata
->returnreceiptto
)
1517 memusage
+= strlen(msginfo
->extradata
->returnreceiptto
);
1519 if (msginfo
->extradata
->partial_recv
)
1520 memusage
+= strlen(msginfo
->extradata
->partial_recv
);
1521 if (msginfo
->extradata
->account_server
)
1522 memusage
+= strlen(msginfo
->extradata
->account_server
);
1523 if (msginfo
->extradata
->account_login
)
1524 memusage
+= strlen(msginfo
->extradata
->account_login
);
1525 if (msginfo
->extradata
->resent_from
)
1526 memusage
+= strlen(msginfo
->extradata
->resent_from
);
1528 if (msginfo
->extradata
->list_post
)
1529 memusage
+= strlen(msginfo
->extradata
->list_post
);
1530 if (msginfo
->extradata
->list_subscribe
)
1531 memusage
+= strlen(msginfo
->extradata
->list_subscribe
);
1532 if (msginfo
->extradata
->list_unsubscribe
)
1533 memusage
+= strlen(msginfo
->extradata
->list_unsubscribe
);
1534 if (msginfo
->extradata
->list_help
)
1535 memusage
+= strlen(msginfo
->extradata
->list_help
);
1536 if (msginfo
->extradata
->list_archive
)
1537 memusage
+= strlen(msginfo
->extradata
->list_archive
);
1538 if (msginfo
->extradata
->list_owner
)
1539 memusage
+= strlen(msginfo
->extradata
->list_owner
);
1544 static gint
procmsg_send_message_queue_full(const gchar
*file
, gboolean keep_session
, gchar
**errstr
,
1545 FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1547 static HeaderEntry qentry
[] = {
1548 {"S:", NULL
, FALSE
}, /* 0 */
1549 {"SSV:", NULL
, FALSE
},
1550 {"R:", NULL
, FALSE
},
1551 {"NG:", NULL
, FALSE
},
1552 {"MAID:", NULL
, FALSE
},
1553 {"NAID:", NULL
, FALSE
}, /* 5 */
1554 {"SCF:", NULL
, FALSE
},
1555 {"RMID:", NULL
, FALSE
},
1556 {"FMID:", NULL
, FALSE
},
1557 {"X-Claws-Privacy-System:", NULL
, FALSE
},
1558 {"X-Claws-Encrypt:", NULL
, FALSE
}, /* 10 */
1559 {"X-Claws-Encrypt-Data:", NULL
, FALSE
},
1560 {"X-Claws-End-Special-Headers:", NULL
, FALSE
},
1561 {"X-Sylpheed-Privacy-System:", NULL
, FALSE
},
1562 {"X-Sylpheed-Encrypt:", NULL
, FALSE
},
1563 {"X-Sylpheed-Encrypt-Data:", NULL
, FALSE
}, /* 15 */
1564 {"X-Sylpheed-End-Special-Headers:", NULL
, FALSE
},
1565 {NULL
, NULL
, FALSE
}};
1568 gint mailval
= 0, newsval
= 0;
1570 gchar
*smtpserver
= NULL
;
1571 GSList
*to_list
= NULL
;
1572 GSList
*newsgroup_list
= NULL
;
1573 gchar
*savecopyfolder
= NULL
;
1574 gchar
*replymessageid
= NULL
;
1575 gchar
*fwdmessageid
= NULL
;
1578 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
1579 gboolean encrypt
= FALSE
;
1582 cm_return_val_if_fail(file
!= NULL
, -1);
1584 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
1585 FILE_OP_ERROR(file
, "claws_fopen");
1587 if (*errstr
) g_free(*errstr
);
1588 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1593 while ((hnum
= procheader_get_one_field(&buf
, fp
, qentry
)) != -1 && buf
!= NULL
) {
1594 gchar
*p
= buf
+ strlen(qentry
[hnum
].name
);
1602 if (smtpserver
== NULL
)
1603 smtpserver
= g_strdup(p
);
1606 to_list
= address_list_append(to_list
, p
);
1609 newsgroup_list
= newsgroup_list_append(newsgroup_list
, p
);
1611 case Q_MAIL_ACCOUNT_ID
:
1612 mailac
= account_find_from_id(atoi(p
));
1614 case Q_NEWS_ACCOUNT_ID
:
1615 newsac
= account_find_from_id(atoi(p
));
1617 case Q_SAVE_COPY_FOLDER
:
1618 if (savecopyfolder
== NULL
)
1619 savecopyfolder
= g_strdup(p
);
1621 case Q_REPLY_MESSAGE_ID
:
1622 if (replymessageid
== NULL
)
1623 replymessageid
= g_strdup(p
);
1625 case Q_FWD_MESSAGE_ID
:
1626 if (fwdmessageid
== NULL
)
1627 fwdmessageid
= g_strdup(p
);
1635 case Q_CLAWS_HDRS_OLD
:
1636 /* end of special headers reached */
1638 goto send_mail
; /* can't "break;break;" */
1644 filepos
= ftell(fp
);
1646 FILE_OP_ERROR(file
, "ftell");
1648 if (*errstr
) g_free(*errstr
);
1649 *errstr
= g_strdup_printf(_("Couldn't open file %s."), file
);
1655 debug_print("Sending message by mail\n");
1658 if (*errstr
) g_free(*errstr
);
1659 *errstr
= g_strdup_printf(_("Queued message header is broken."));
1662 } else if (mailac
&& mailac
->use_mail_command
&&
1663 mailac
->mail_command
&& (* mailac
->mail_command
)) {
1664 mailval
= send_message_local(mailac
->mail_command
, fp
);
1667 mailac
= account_find_from_smtp_server(from
, smtpserver
);
1669 g_warning("account not found, "
1670 "using current account...");
1671 mailac
= cur_account
;
1676 mailval
= send_message_smtp_full(mailac
, to_list
, fp
, keep_session
);
1677 if (mailval
== -1 && errstr
) {
1678 if (*errstr
) g_free(*errstr
);
1679 *errstr
= g_strdup_printf(_("An error happened during SMTP session."));
1682 PrefsAccount tmp_ac
;
1684 g_warning("account not found");
1686 memset(&tmp_ac
, 0, sizeof(PrefsAccount
));
1687 tmp_ac
.address
= from
;
1688 tmp_ac
.smtp_server
= smtpserver
;
1689 tmp_ac
.smtpport
= SMTP_PORT
;
1690 mailval
= send_message_smtp(&tmp_ac
, to_list
, fp
);
1691 if (mailval
== -1 && errstr
) {
1692 if (*errstr
) g_free(*errstr
);
1693 *errstr
= g_strdup_printf(_("No specific account has been found to "
1694 "send, and an error happened during SMTP session."));
1698 } else if (!to_list
&& !newsgroup_list
) {
1700 if (*errstr
) g_free(*errstr
);
1701 *errstr
= g_strdup(_("Couldn't determine sending information. "
1702 "Maybe the email hasn't been generated by Claws Mail."));
1707 if (fseek(fp
, filepos
, SEEK_SET
) < 0) {
1708 FILE_OP_ERROR(file
, "fseek");
1712 if (newsgroup_list
&& newsac
&& (mailval
== 0)) {
1715 gchar buf
[BUFFSIZE
];
1718 /* write to temporary file */
1719 tmp
= g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1720 G_DIR_SEPARATOR
, file
);
1721 if ((tmpfp
= claws_fopen(tmp
, "wb")) == NULL
) {
1722 FILE_OP_ERROR(tmp
, "claws_fopen");
1724 alertpanel_error(_("Couldn't create temporary file for news sending."));
1726 if (change_file_mode_rw(tmpfp
, tmp
) < 0) {
1727 FILE_OP_ERROR(tmp
, "chmod");
1728 g_warning("can't change file mode");
1731 while ((newsval
== 0) && claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1732 if (claws_fputs(buf
, tmpfp
) == EOF
) {
1733 FILE_OP_ERROR(tmp
, "claws_fputs");
1736 if (*errstr
) g_free(*errstr
);
1737 *errstr
= g_strdup_printf(_("Error when writing temporary file for news sending."));
1741 claws_safe_fclose(tmpfp
);
1744 debug_print("Sending message by news\n");
1746 folder
= FOLDER(newsac
->folder
);
1748 newsval
= news_post(folder
, tmp
);
1749 if (newsval
< 0 && errstr
) {
1750 if (*errstr
) g_free(*errstr
);
1751 *errstr
= g_strdup_printf(_("Error occurred while posting the message to %s."),
1752 newsac
->nntp_server
);
1762 /* update session statistics */
1763 if (mailval
== 0 && newsval
== 0) {
1764 /* update session stats */
1766 session_stats
.replied
++;
1767 else if (fwdmessageid
)
1768 session_stats
.forwarded
++;
1770 session_stats
.sent
++;
1773 /* save message to outbox */
1774 if (mailval
== 0 && newsval
== 0 && savecopyfolder
) {
1775 debug_print("saving sent message to %s...\n", savecopyfolder
);
1777 if (!encrypt
|| !mailac
->save_encrypted_as_clear_text
) {
1778 outbox
= folder_find_item_from_identifier(savecopyfolder
);
1781 outbox
= folder_get_default_outbox();
1782 if (outbox
!= NULL
) {
1783 id
= folder_item_get_identifier(outbox
);
1784 debug_print("%s not found, using %s\n", savecopyfolder
, id
);
1787 debug_print("could not find outbox\n");
1790 /* Mail was not saved to outbox before encrypting, save it now. */
1791 gboolean saved
= FALSE
;
1792 *queued_removed
= FALSE
;
1793 if (queue
&& msgnum
> 0) {
1794 MsgInfo
*queued_mail
= folder_item_get_msginfo(queue
, msgnum
);
1795 if (folder_item_move_msg(outbox
, queued_mail
) >= 0) {
1796 debug_print("moved queued mail %d to sent folder\n", msgnum
);
1798 *queued_removed
= TRUE
;
1799 } else if (folder_item_copy_msg(outbox
, queued_mail
) >= 0) {
1800 debug_print("copied queued mail %d to sent folder\n", msgnum
);
1803 procmsg_msginfo_free(&queued_mail
);
1806 debug_print("resaving queued mail to sent folder\n");
1807 procmsg_save_to_outbox(outbox
, file
, TRUE
);
1812 if (replymessageid
!= NULL
|| fwdmessageid
!= NULL
) {
1816 if (replymessageid
!= NULL
)
1817 tokens
= g_strsplit(replymessageid
, "\t", 0);
1819 tokens
= g_strsplit(fwdmessageid
, "\t", 0);
1820 item
= folder_find_item_from_identifier(tokens
[0]);
1822 /* check if queued message has valid folder and message id */
1823 if (item
!= NULL
&& tokens
[2] != NULL
) {
1826 msginfo
= folder_item_get_msginfo(item
, atoi(tokens
[1]));
1828 /* check if referring message exists and has a message id */
1829 if ((msginfo
!= NULL
) &&
1830 (msginfo
->msgid
!= NULL
) &&
1831 (strcmp(msginfo
->msgid
, tokens
[2]) != 0)) {
1832 procmsg_msginfo_free(&msginfo
);
1836 if (msginfo
== NULL
) {
1837 msginfo
= folder_item_get_msginfo_by_msgid(item
, tokens
[2]);
1840 if (msginfo
!= NULL
) {
1841 if (replymessageid
!= NULL
) {
1842 MsgPermFlags to_unset
= 0;
1844 if (prefs_common
.mark_as_read_on_new_window
)
1845 to_unset
= (MSG_NEW
|MSG_UNREAD
);
1847 procmsg_msginfo_unset_flags(msginfo
, to_unset
, 0);
1848 procmsg_msginfo_set_flags(msginfo
, MSG_REPLIED
, 0);
1850 procmsg_msginfo_set_flags(msginfo
, MSG_FORWARDED
, 0);
1852 procmsg_msginfo_free(&msginfo
);
1860 slist_free_strings_full(to_list
);
1861 slist_free_strings_full(newsgroup_list
);
1862 g_free(savecopyfolder
);
1863 g_free(replymessageid
);
1864 g_free(fwdmessageid
);
1866 return (newsval
!= 0 ? newsval
: mailval
);
1869 gint
procmsg_send_message_queue(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1871 gint result
= procmsg_send_message_queue_full(file
, FALSE
, errstr
, queue
, msgnum
, queued_removed
);
1872 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1873 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1877 gint
procmsg_send_message_queue_with_lock(const gchar
*file
, gchar
**errstr
, FolderItem
*queue
, gint msgnum
, gboolean
*queued_removed
)
1880 if (procmsg_queue_lock(errstr
)) {
1881 val
= procmsg_send_message_queue(file
, errstr
, queue
, msgnum
, queued_removed
);
1882 procmsg_queue_unlock();
1888 static void update_folder_msg_counts(FolderItem
*item
, MsgInfo
*msginfo
, MsgPermFlags old_flags
)
1890 MsgPermFlags new_flags
= msginfo
->flags
.perm_flags
;
1893 if (!(old_flags
& MSG_NEW
) && (new_flags
& MSG_NEW
)) {
1897 if ((old_flags
& MSG_NEW
) && !(new_flags
& MSG_NEW
)) {
1902 if (!(old_flags
& MSG_UNREAD
) && (new_flags
& MSG_UNREAD
)) {
1903 item
->unread_msgs
++;
1904 if (procmsg_msg_has_marked_parent(msginfo
))
1905 item
->unreadmarked_msgs
++;
1908 if ((old_flags
& MSG_UNREAD
) && !(new_flags
& MSG_UNREAD
)) {
1909 item
->unread_msgs
--;
1910 if (procmsg_msg_has_marked_parent(msginfo
))
1911 item
->unreadmarked_msgs
--;
1915 if (!(old_flags
& MSG_MARKED
) && (new_flags
& MSG_MARKED
)) {
1916 procmsg_update_unread_children(msginfo
, TRUE
);
1917 item
->marked_msgs
++;
1920 if ((old_flags
& MSG_MARKED
) && !(new_flags
& MSG_MARKED
)) {
1921 procmsg_update_unread_children(msginfo
, FALSE
);
1922 item
->marked_msgs
--;
1925 if (!(old_flags
& MSG_REPLIED
) && (new_flags
& MSG_REPLIED
)) {
1926 item
->replied_msgs
++;
1929 if ((old_flags
& MSG_REPLIED
) && !(new_flags
& MSG_REPLIED
)) {
1930 item
->replied_msgs
--;
1933 if (!(old_flags
& MSG_FORWARDED
) && (new_flags
& MSG_FORWARDED
)) {
1934 item
->forwarded_msgs
++;
1937 if ((old_flags
& MSG_FORWARDED
) && !(new_flags
& MSG_FORWARDED
)) {
1938 item
->forwarded_msgs
--;
1941 if (!(old_flags
& MSG_LOCKED
) && (new_flags
& MSG_LOCKED
)) {
1942 item
->locked_msgs
++;
1945 if ((old_flags
& MSG_LOCKED
) && !(new_flags
& MSG_LOCKED
)) {
1946 item
->locked_msgs
--;
1949 if ((old_flags
& MSG_IGNORE_THREAD
) && !(new_flags
& MSG_IGNORE_THREAD
)) {
1950 item
->ignored_msgs
--;
1953 if (!(old_flags
& MSG_IGNORE_THREAD
) && (new_flags
& MSG_IGNORE_THREAD
)) {
1954 item
->ignored_msgs
++;
1957 if ((old_flags
& MSG_WATCH_THREAD
) && !(new_flags
& MSG_WATCH_THREAD
)) {
1958 item
->watched_msgs
--;
1961 if (!(old_flags
& MSG_WATCH_THREAD
) && (new_flags
& MSG_WATCH_THREAD
)) {
1962 item
->watched_msgs
++;
1966 void procmsg_msginfo_set_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
1969 MsgInfoUpdate msginfo_update
;
1970 MsgPermFlags perm_flags_new
, perm_flags_old
;
1971 MsgTmpFlags tmp_flags_old
;
1973 cm_return_if_fail(msginfo
!= NULL
);
1974 item
= msginfo
->folder
;
1975 cm_return_if_fail(item
!= NULL
);
1977 debug_print("Setting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
1979 /* Perm Flags handling */
1980 perm_flags_old
= msginfo
->flags
.perm_flags
;
1981 perm_flags_new
= msginfo
->flags
.perm_flags
| perm_flags
;
1982 if ((perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
1983 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
1985 if ((perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
1986 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
1989 if (perm_flags_old
!= perm_flags_new
) {
1990 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
1992 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
1993 summary_update_unread(mainwindow_get_mainwindow()->summaryview
, NULL
);
1996 /* Tmp flags handling */
1997 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
1998 msginfo
->flags
.tmp_flags
|= tmp_flags
;
2000 /* update notification */
2001 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2002 msginfo_update
.msginfo
= msginfo
;
2003 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2004 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2005 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2009 void procmsg_msginfo_unset_flags(MsgInfo
*msginfo
, MsgPermFlags perm_flags
, MsgTmpFlags tmp_flags
)
2012 MsgInfoUpdate msginfo_update
;
2013 MsgPermFlags perm_flags_new
, perm_flags_old
;
2014 MsgTmpFlags tmp_flags_old
;
2016 cm_return_if_fail(msginfo
!= NULL
);
2017 item
= msginfo
->folder
;
2018 cm_return_if_fail(item
!= NULL
);
2020 debug_print("Unsetting flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
2022 /* Perm Flags handling */
2023 perm_flags_old
= msginfo
->flags
.perm_flags
;
2024 perm_flags_new
= msginfo
->flags
.perm_flags
& ~perm_flags
;
2026 if (perm_flags_old
!= perm_flags_new
) {
2027 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2029 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2032 /* Tmp flags hanlding */
2033 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2034 msginfo
->flags
.tmp_flags
&= ~tmp_flags
;
2036 /* update notification */
2037 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2038 msginfo_update
.msginfo
= msginfo
;
2039 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2040 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2041 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2045 void procmsg_msginfo_change_flags(MsgInfo
*msginfo
,
2046 MsgPermFlags add_perm_flags
, MsgTmpFlags add_tmp_flags
,
2047 MsgPermFlags rem_perm_flags
, MsgTmpFlags rem_tmp_flags
)
2050 MsgInfoUpdate msginfo_update
;
2051 MsgPermFlags perm_flags_new
, perm_flags_old
;
2052 MsgTmpFlags tmp_flags_old
;
2054 cm_return_if_fail(msginfo
!= NULL
);
2055 item
= msginfo
->folder
;
2056 cm_return_if_fail(item
!= NULL
);
2058 debug_print("Changing flags for message %d in folder %s\n", msginfo
->msgnum
, item
->path
);
2060 /* Perm Flags handling */
2061 perm_flags_old
= msginfo
->flags
.perm_flags
;
2062 perm_flags_new
= (msginfo
->flags
.perm_flags
& ~rem_perm_flags
) | add_perm_flags
;
2063 if ((add_perm_flags
& MSG_IGNORE_THREAD
) || (perm_flags_old
& MSG_IGNORE_THREAD
)) {
2064 perm_flags_new
&= ~(MSG_NEW
| MSG_UNREAD
);
2066 if ((add_perm_flags
& MSG_WATCH_THREAD
) || (perm_flags_old
& MSG_WATCH_THREAD
)) {
2067 perm_flags_new
&= ~(MSG_IGNORE_THREAD
);
2070 if (perm_flags_old
!= perm_flags_new
) {
2071 folder_item_change_msg_flags(msginfo
->folder
, msginfo
, perm_flags_new
);
2073 update_folder_msg_counts(item
, msginfo
, perm_flags_old
);
2077 /* Tmp flags handling */
2078 tmp_flags_old
= msginfo
->flags
.tmp_flags
;
2079 msginfo
->flags
.tmp_flags
&= ~rem_tmp_flags
;
2080 msginfo
->flags
.tmp_flags
|= add_tmp_flags
;
2082 /* update notification */
2083 if ((perm_flags_old
!= perm_flags_new
) || (tmp_flags_old
!= msginfo
->flags
.tmp_flags
)) {
2084 msginfo_update
.msginfo
= msginfo
;
2085 msginfo_update
.flags
= MSGINFO_UPDATE_FLAGS
;
2086 hooks_invoke(MSGINFO_UPDATE_HOOKLIST
, &msginfo_update
);
2087 folder_item_update(msginfo
->folder
, F_ITEM_UPDATE_MSGCNT
);
2092 *\brief check for flags (e.g. mark) in prior msgs of current thread
2094 *\param info Current message
2095 *\param perm_flags Flags to be checked
2096 *\param parentmsgs Hash of prior msgs to avoid loops
2098 *\return gboolean TRUE if perm_flags are found
2100 static gboolean
procmsg_msg_has_flagged_parent_real(MsgInfo
*info
,
2101 MsgPermFlags perm_flags
, GHashTable
*parentmsgs
)
2105 cm_return_val_if_fail(info
!= NULL
, FALSE
);
2107 if (info
!= NULL
&& info
->folder
!= NULL
&& info
->inreplyto
!= NULL
) {
2108 tmp
= folder_item_get_msginfo_by_msgid(info
->folder
,
2110 if (tmp
&& (tmp
->flags
.perm_flags
& perm_flags
)) {
2111 procmsg_msginfo_free(&tmp
);
2113 } else if (tmp
!= NULL
) {
2116 if (g_hash_table_lookup(parentmsgs
, info
)) {
2117 debug_print("loop detected: %d\n",
2121 g_hash_table_insert(parentmsgs
, info
, "1");
2122 result
= procmsg_msg_has_flagged_parent_real(
2123 tmp
, perm_flags
, parentmsgs
);
2125 procmsg_msginfo_free(&tmp
);
2135 *\brief Callback for cleaning up hash of parentmsgs
2137 static gboolean
parentmsgs_hash_remove(gpointer key
,
2145 *\brief Set up list of parentmsgs
2146 * See procmsg_msg_has_flagged_parent_real()
2148 gboolean
procmsg_msg_has_flagged_parent(MsgInfo
*info
, MsgPermFlags perm_flags
)
2151 static GHashTable
*parentmsgs
= NULL
;
2153 if (parentmsgs
== NULL
)
2154 parentmsgs
= g_hash_table_new(NULL
, NULL
);
2156 result
= procmsg_msg_has_flagged_parent_real(info
, perm_flags
, parentmsgs
);
2157 g_hash_table_foreach_remove(parentmsgs
, parentmsgs_hash_remove
, NULL
);
2163 *\brief Check if msgs prior in thread are marked
2164 * See procmsg_msg_has_flagged_parent_real()
2166 gboolean
procmsg_msg_has_marked_parent(MsgInfo
*info
)
2168 return procmsg_msg_has_flagged_parent(info
, MSG_MARKED
);
2172 static GSList
*procmsg_find_children_func(MsgInfo
*info
,
2173 GSList
*children
, GSList
*all
)
2177 cm_return_val_if_fail(info
!=NULL
, children
);
2178 if (info
->msgid
== NULL
)
2181 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2182 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2183 if (tmp
->inreplyto
&& !strcmp(tmp
->inreplyto
, info
->msgid
)) {
2184 /* Check if message is already in the list */
2185 if ((children
== NULL
) ||
2186 (g_slist_index(children
, tmp
) == -1)) {
2187 children
= g_slist_prepend(children
,
2188 procmsg_msginfo_new_ref(tmp
));
2189 children
= procmsg_find_children_func(tmp
,
2198 static GSList
*procmsg_find_children (MsgInfo
*info
)
2203 cm_return_val_if_fail(info
!=NULL
, NULL
);
2204 all
= folder_item_get_msg_list(info
->folder
);
2205 children
= procmsg_find_children_func(info
, NULL
, all
);
2206 if (children
!= NULL
) {
2207 for (cur
= all
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2208 /* this will not free the used pointers
2209 created with procmsg_msginfo_new_ref */
2210 procmsg_msginfo_free((MsgInfo
**)&(cur
->data
));
2218 static void procmsg_update_unread_children(MsgInfo
*info
, gboolean newly_marked
)
2220 GSList
*children
= procmsg_find_children(info
);
2222 for (cur
= children
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
2223 MsgInfo
*tmp
= (MsgInfo
*)cur
->data
;
2224 if(MSG_IS_UNREAD(tmp
->flags
) && !MSG_IS_IGNORE_THREAD(tmp
->flags
)) {
2226 info
->folder
->unreadmarked_msgs
++;
2228 info
->folder
->unreadmarked_msgs
--;
2229 folder_item_update(info
->folder
, F_ITEM_UPDATE_MSGCNT
);
2231 procmsg_msginfo_free(&tmp
);
2233 g_slist_free(children
);
2237 * Set the destination folder for a copy or move operation
2239 * \param msginfo The message which's destination folder is changed
2240 * \param to_folder The destination folder for the operation
2242 void procmsg_msginfo_set_to_folder(MsgInfo
*msginfo
, FolderItem
*to_folder
)
2244 if(msginfo
->to_folder
!= NULL
) {
2245 msginfo
->to_folder
->op_count
--;
2246 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2248 msginfo
->to_folder
= to_folder
;
2249 if(to_folder
!= NULL
) {
2250 to_folder
->op_count
++;
2251 folder_item_update(msginfo
->to_folder
, F_ITEM_UPDATE_MSGCNT
);
2256 * Apply filtering actions to the msginfo
2258 * \param msginfo The MsgInfo describing the message that should be filtered
2259 * \return TRUE if the message was moved and MsgInfo is now invalid,
2262 static gboolean
procmsg_msginfo_filter(MsgInfo
*msginfo
, PrefsAccount
* ac_prefs
)
2264 MailFilteringData mail_filtering_data
;
2266 mail_filtering_data
.msginfo
= msginfo
;
2267 mail_filtering_data
.msglist
= NULL
;
2268 mail_filtering_data
.filtered
= NULL
;
2269 mail_filtering_data
.unfiltered
= NULL
;
2270 mail_filtering_data
.account
= ac_prefs
;
2272 if (!ac_prefs
|| ac_prefs
->filterhook_on_recv
)
2273 if (hooks_invoke(MAIL_FILTERING_HOOKLIST
, &mail_filtering_data
))
2276 /* filter if enabled in prefs or move to inbox if not */
2277 if((filtering_rules
!= NULL
) &&
2278 filter_message_by_msginfo(filtering_rules
, msginfo
, ac_prefs
,
2279 FILTERING_INCORPORATION
, NULL
)) {
2286 void procmsg_msglist_filter(GSList
*list
, PrefsAccount
*ac
,
2287 GSList
**filtered
, GSList
**unfiltered
,
2290 GSList
*cur
, *to_do
= NULL
;
2291 gint total
= 0, curnum
= 0;
2292 MailFilteringData mail_filtering_data
;
2294 cm_return_if_fail(filtered
!= NULL
);
2295 cm_return_if_fail(unfiltered
!= NULL
);
2303 total
= g_slist_length(list
);
2307 *unfiltered
= g_slist_copy(list
);
2311 statusbar_print_all(_("Filtering messages...\n"));
2313 mail_filtering_data
.msginfo
= NULL
;
2314 mail_filtering_data
.msglist
= list
;
2315 mail_filtering_data
.filtered
= NULL
;
2316 mail_filtering_data
.unfiltered
= NULL
;
2317 mail_filtering_data
.account
= ac
;
2319 if (!ac
|| ac
->filterhook_on_recv
)
2320 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST
, &mail_filtering_data
);
2322 if (mail_filtering_data
.filtered
== NULL
&&
2323 mail_filtering_data
.unfiltered
== NULL
) {
2324 /* nothing happened */
2325 debug_print(MAIL_LISTFILTERING_HOOKLIST
" did nothing. filtering whole list normally.\n");
2328 if (mail_filtering_data
.filtered
!= NULL
) {
2329 /* keep track of what's been filtered by the hooks */
2330 debug_print(MAIL_LISTFILTERING_HOOKLIST
" filtered some stuff. total %d filtered %d unfilt %d.\n",
2331 g_slist_length(list
),
2332 g_slist_length(mail_filtering_data
.filtered
),
2333 g_slist_length(mail_filtering_data
.unfiltered
));
2335 *filtered
= g_slist_copy(mail_filtering_data
.filtered
);
2337 if (mail_filtering_data
.unfiltered
!= NULL
) {
2338 /* what the hooks didn't handle will go in filtered or
2339 * unfiltered in the next loop */
2340 debug_print(MAIL_LISTFILTERING_HOOKLIST
" left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2341 g_slist_length(list
),
2342 g_slist_length(mail_filtering_data
.filtered
),
2343 g_slist_length(mail_filtering_data
.unfiltered
));
2344 to_do
= mail_filtering_data
.unfiltered
;
2347 for (cur
= to_do
; cur
; cur
= cur
->next
) {
2348 MsgInfo
*info
= (MsgInfo
*)cur
->data
;
2349 if (procmsg_msginfo_filter(info
, ac
))
2350 *filtered
= g_slist_prepend(*filtered
, info
);
2352 *unfiltered
= g_slist_prepend(*unfiltered
, info
);
2353 statusbar_progress_all(curnum
++, total
, prefs_common
.statusbar_update_step
);
2356 g_slist_free(mail_filtering_data
.filtered
);
2357 g_slist_free(mail_filtering_data
.unfiltered
);
2359 *filtered
= g_slist_reverse(*filtered
);
2360 *unfiltered
= g_slist_reverse(*unfiltered
);
2362 statusbar_progress_all(0,0,0);
2363 statusbar_pop_all();
2366 MsgInfo
*procmsg_msginfo_new_from_mimeinfo(MsgInfo
*src_msginfo
, MimeInfo
*mimeinfo
)
2368 MsgInfo
*tmp_msginfo
= NULL
;
2369 MsgFlags flags
= {0, 0};
2370 gchar
*tmpfile
= get_tmp_file();
2371 FILE *fp
= claws_fopen(tmpfile
, "wb");
2373 if (!mimeinfo
|| mimeinfo
->type
!= MIMETYPE_MESSAGE
||
2374 g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
2375 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2382 if (fp
&& procmime_write_mimeinfo(mimeinfo
, fp
) >= 0) {
2383 claws_safe_fclose(fp
);
2385 tmp_msginfo
= procheader_parse_file(
2390 claws_safe_fclose(fp
);
2392 if (tmp_msginfo
!= NULL
) {
2394 tmp_msginfo
->folder
= src_msginfo
->folder
;
2395 tmp_msginfo
->plaintext_file
= g_strdup(tmpfile
);
2397 g_warning("procmsg_msginfo_new_from_mimeinfo(): can't generate new msginfo");
2405 static GSList
*spam_learners
= NULL
;
2407 void procmsg_register_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2409 if (!g_slist_find(spam_learners
, learn_func
))
2410 spam_learners
= g_slist_append(spam_learners
, learn_func
);
2411 if (mainwindow_get_mainwindow()) {
2412 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2413 summary_set_menu_sensitive(
2414 mainwindow_get_mainwindow()->summaryview
);
2415 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2419 void procmsg_unregister_spam_learner (int (*learn_func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
))
2421 spam_learners
= g_slist_remove(spam_learners
, learn_func
);
2422 if (mainwindow_get_mainwindow()) {
2423 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2424 summary_set_menu_sensitive(
2425 mainwindow_get_mainwindow()->summaryview
);
2426 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2430 gboolean
procmsg_spam_can_learn(void)
2432 return g_slist_length(spam_learners
) > 0;
2435 int procmsg_spam_learner_learn (MsgInfo
*info
, GSList
*list
, gboolean spam
)
2437 GSList
*cur
= spam_learners
;
2439 for (; cur
; cur
= cur
->next
) {
2440 int ((*func
)(MsgInfo
*info
, GSList
*list
, gboolean spam
)) = cur
->data
;
2441 ret
|= func(info
, list
, spam
);
2446 static gchar
*spam_folder_item
= NULL
;
2447 static FolderItem
* (*procmsg_spam_get_folder_func
)(MsgInfo
*msginfo
) = NULL
;
2448 void procmsg_spam_set_folder (const char *item_identifier
, FolderItem
*(*spam_get_folder_func
)(MsgInfo
*info
))
2450 g_free(spam_folder_item
);
2451 if (item_identifier
)
2452 spam_folder_item
= g_strdup(item_identifier
);
2454 spam_folder_item
= NULL
;
2455 if (spam_get_folder_func
!= NULL
)
2456 procmsg_spam_get_folder_func
= spam_get_folder_func
;
2458 procmsg_spam_get_folder_func
= NULL
;
2461 FolderItem
*procmsg_spam_get_folder (MsgInfo
*msginfo
)
2463 FolderItem
*item
= NULL
;
2465 if (procmsg_spam_get_folder_func
)
2466 item
= procmsg_spam_get_folder_func(msginfo
);
2467 if (item
== NULL
&& spam_folder_item
)
2468 item
= folder_find_item_from_identifier(spam_folder_item
);
2470 item
= folder_get_default_trash();
2474 static void item_has_queued_mails(FolderItem
*item
, gpointer data
)
2476 gboolean
*result
= (gboolean
*)data
;
2477 if (*result
== TRUE
)
2479 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
2480 if (item
->total_msgs
== 0)
2483 GSList
*msglist
= folder_item_get_msg_list(item
);
2485 for (cur
= msglist
; cur
; cur
= cur
->next
) {
2486 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
2487 if (!MSG_IS_DELETED(msginfo
->flags
) &&
2488 !MSG_IS_LOCKED(msginfo
->flags
)) {
2493 procmsg_msg_list_free(msglist
);
2498 gboolean
procmsg_have_queued_mails_fast (void)
2500 gboolean result
= FALSE
;
2501 folder_func_to_all_folders(item_has_queued_mails
, &result
);
2505 static void item_has_trashed_mails(FolderItem
*item
, gpointer data
)
2507 gboolean
*result
= (gboolean
*)data
;
2508 if (*result
== TRUE
)
2510 if (folder_has_parent_of_type(item
, F_TRASH
) && item
->total_msgs
> 0)
2514 gboolean
procmsg_have_trashed_mails_fast (void)
2516 gboolean result
= FALSE
;
2517 folder_func_to_all_folders(item_has_trashed_mails
, &result
);
2521 gchar
*procmsg_msginfo_get_tags_str(MsgInfo
*msginfo
)
2529 if (msginfo
->tags
== NULL
)
2531 for (cur
= msginfo
->tags
; cur
; cur
= cur
->next
) {
2532 const gchar
*tag
= tags_get_tag(GPOINTER_TO_INT(cur
->data
));
2536 tags
= g_strdup(tag
);
2538 int olen
= strlen(tags
);
2539 int nlen
= olen
+ strlen(tag
) + 2 /* strlen(", ") */;
2540 tags
= g_realloc(tags
, nlen
+1);
2543 strcpy(tags
+olen
, ", ");
2544 strcpy(tags
+olen
+2, tag
);
2551 void procmsg_msginfo_update_tags(MsgInfo
*msginfo
, gboolean set
, gint id
)
2559 msginfo
->tags
= g_slist_remove(
2561 GINT_TO_POINTER(id
));
2562 changed
.data
= GINT_TO_POINTER(id
);
2563 changed
.next
= NULL
;
2564 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, &changed
);
2566 if (!g_slist_find(msginfo
->tags
, GINT_TO_POINTER(id
))) {
2567 msginfo
->tags
= g_slist_append(
2569 GINT_TO_POINTER(id
));
2571 changed
.data
= GINT_TO_POINTER(id
);
2572 changed
.next
= NULL
;
2573 folder_item_commit_tags(msginfo
->folder
, msginfo
, &changed
, NULL
);
2578 void procmsg_msginfo_clear_tags(MsgInfo
*msginfo
)
2580 GSList
*unset
= msginfo
->tags
;
2581 msginfo
->tags
= NULL
;
2582 folder_item_commit_tags(msginfo
->folder
, msginfo
, NULL
, unset
);
2583 g_slist_free(unset
);