remove unnecessary cm_return_val_if_fail()
[claws.git] / src / procmsg.c
blobfee027da9773f9ed10cf1e952cbabf771fc3f47c
1 /*
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/>.
19 #include "defs.h"
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <math.h>
28 #include "main.h"
29 #include "utils.h"
30 #include "procmsg.h"
31 #include "procheader.h"
32 #include "send_message.h"
33 #include "procmime.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
37 #include "folder.h"
38 #include "prefs_common.h"
39 #include "account.h"
40 #include "alertpanel.h"
41 #include "news.h"
42 #include "hooks.h"
43 #include "msgcache.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
47 #include "log.h"
48 #include "tags.h"
49 #include "timing.h"
50 #include "inc.h"
51 #include "privacy.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);
60 enum
62 Q_SENDER = 0,
63 Q_SMTPSERVER = 1,
64 Q_RECIPIENTS = 2,
65 Q_NEWSGROUPS = 3,
66 Q_MAIL_ACCOUNT_ID = 4,
67 Q_NEWS_ACCOUNT_ID = 5,
68 Q_SAVE_COPY_FOLDER = 6,
69 Q_REPLY_MESSAGE_ID = 7,
70 Q_FWD_MESSAGE_ID = 8,
71 Q_PRIVACY_SYSTEM = 9,
72 Q_ENCRYPT = 10,
73 Q_ENCRYPT_DATA = 11,
74 Q_CLAWS_HDRS = 12,
75 Q_PRIVACY_SYSTEM_OLD = 13,
76 Q_ENCRYPT_OLD = 14,
77 Q_ENCRYPT_DATA_OLD = 15,
78 Q_CLAWS_HDRS_OLD = 16,
81 void procmsg_msg_list_free(GSList *mlist)
83 GSList *cur;
84 MsgInfo *msginfo;
86 for (cur = mlist; cur != NULL; cur = cur->next) {
87 msginfo = (MsgInfo *)cur->data;
88 procmsg_msginfo_free(&msginfo);
90 g_slist_free(mlist);
93 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
95 GSList *cur = NULL;
96 GSList *nums = NULL;
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);
106 struct MarkSum {
107 gint *new_msgs;
108 gint *unread_msgs;
109 gint *total_msgs;
110 gint *min;
111 gint *max;
112 gint first;
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)
129 gchar *subject;
130 MsgInfo *msginfo;
131 GSList *list = NULL;
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;
139 if (subject == NULL)
140 return;
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)
151 gchar *subject;
152 GSList *list, *cur;
153 GNode *node = NULL, *hashtable_node = NULL;
154 gint prefix_length;
155 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
156 gboolean match;
158 cm_return_val_if_fail(hashtable != NULL, NULL);
160 subject = msginfo->subject;
161 if (subject == NULL)
162 return NULL;
163 prefix_length = subject_get_prefix_length(subject);
164 if (prefix_length <= 0)
165 return NULL;
166 subject += prefix_length;
168 list = g_hash_table_lookup(hashtable, subject);
169 if (list == NULL)
170 return NULL;
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;
176 match = FALSE;
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)))
183 match = TRUE;
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)
189 match = FALSE;
191 /* can add new tests for all matching
192 nodes found by subject */
194 if (match) {
195 node = hashtable_node;
196 best_msginfo = hashtable_msginfo;
200 return node;
203 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
205 g_slist_free(value);
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;
214 MsgInfo *msginfo;
215 const gchar *msgid;
216 GSList *reflist;
217 START_TIMING("");
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;
227 parent = root;
229 if (msginfo->inreplyto) {
230 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
231 if (parent == NULL) {
232 parent = root;
235 node = g_node_insert_data_before
236 (parent, parent == root ? parent->children : NULL,
237 msginfo);
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; ) {
249 next = node->next;
250 msginfo = (MsgInfo *)node->data;
251 parent = NULL;
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)
262 break;
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)) {
269 g_node_unlink(node);
270 g_node_insert_before
271 (parent, parent->children, node);
274 node = next;
277 if (prefs_common.thread_by_subject) {
278 START_TIMING("thread by subject");
279 for (node = root->children; node && node != NULL;) {
280 next = node->next;
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
286 * in the tree to
287 find the parent node */
288 if (parent != NULL) {
289 if (g_node_is_ancestor(node, parent))
290 parent = NULL;
291 if (parent == node)
292 parent = NULL;
295 if (parent) {
296 g_node_unlink(node);
297 g_node_append(parent, node);
300 node = next;
302 END_TIMING();
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);
312 END_TIMING();
313 return root;
316 gint procmsg_move_messages(GSList *mlist)
318 GSList *cur, *movelist = NULL;
319 MsgInfo *msginfo;
320 FolderItem *dest = NULL;
321 gint retval = 0;
322 gboolean finished = TRUE;
323 if (!mlist) return 0;
325 folder_item_update_freeze();
327 next_folder:
328 for (cur = mlist; cur != NULL; cur = cur->next) {
329 msginfo = (MsgInfo *)cur->data;
330 if (!msginfo->to_folder) {
331 continue;
332 } else {
333 finished = FALSE;
335 if (!dest) {
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);
340 } else {
341 continue;
343 procmsg_msginfo_set_to_folder(msginfo, NULL);
345 if (movelist) {
346 movelist = g_slist_reverse(movelist);
347 retval |= folder_item_move_msgs(dest, movelist);
348 g_slist_free(movelist);
349 movelist = NULL;
351 if (finished == FALSE) {
352 finished = TRUE;
353 dest = NULL;
354 goto next_folder;
357 folder_item_update_thaw();
358 return retval;
361 void procmsg_copy_messages(GSList *mlist)
363 GSList *cur, *copylist = NULL;
364 MsgInfo *msginfo;
365 FolderItem *dest = NULL;
366 gboolean finished = TRUE;
367 if (!mlist) return;
369 folder_item_update_freeze();
371 next_folder:
372 for (cur = mlist; cur != NULL; cur = cur->next) {
373 msginfo = (MsgInfo *)cur->data;
374 if (!msginfo->to_folder) {
375 continue;
376 } else {
377 finished = FALSE;
379 if (!dest) {
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);
384 } else {
385 continue;
387 procmsg_msginfo_set_to_folder(msginfo, NULL);
389 if (copylist) {
390 copylist = g_slist_reverse(copylist);
391 folder_item_copy_msgs(dest, copylist);
392 g_slist_free(copylist);
393 copylist = NULL;
395 if (finished == FALSE) {
396 finished = TRUE;
397 dest = NULL;
398 goto next_folder;
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);
418 if (!filename)
419 debug_print("can't fetch message %d\n", msginfo->msgnum);
421 return filename;
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,
431 headers, body);
432 if (!filename)
433 debug_print("can't fetch message %d\n", msginfo->msgnum);
435 return filename;
438 GSList *procmsg_get_message_file_list(GSList *mlist)
440 GSList *file_list = NULL;
441 MsgInfo *msginfo;
442 MsgFileInfo *fileinfo;
443 gchar *file;
445 while (mlist != NULL) {
446 msginfo = (MsgInfo *)mlist->data;
447 file = procmsg_get_message_file(msginfo);
448 if (!file) {
449 procmsg_message_file_list_free(file_list);
450 return NULL;
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);
458 mlist = mlist->next;
461 file_list = g_slist_reverse(file_list);
463 return file_list;
466 void procmsg_message_file_list_free(MsgInfoList *file_list)
468 GSList *cur;
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);
476 g_free(fileinfo);
479 g_slist_free(file_list);
482 FILE *procmsg_open_message(MsgInfo *msginfo, gboolean skip_special_headers)
484 FILE *fp;
485 gchar *file;
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)) {
493 g_free(file);
494 file = procmsg_get_message_file(msginfo);
495 if (!file)
496 return NULL;
499 if ((fp = claws_fopen(file, "rb")) == NULL) {
500 FILE_OP_ERROR(file, "claws_fopen");
501 g_free(file);
502 return NULL;
505 g_free(file);
507 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags) ||
508 skip_special_headers == TRUE) {
509 gchar buf[BUFFSIZE];
511 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
512 /* new way */
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:"))))
517 break;
518 /* old way */
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)) {
525 rewind(fp);
526 break;
531 return fp;
534 gboolean procmsg_msg_exist(MsgInfo *msginfo)
536 gboolean ret;
538 if (!msginfo) return FALSE;
540 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
542 return ret;
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}};
557 enum
559 H_X_BEENTHERE = 0,
560 H_X_ML_NAME = 1,
561 H_X_LIST = 2,
562 H_X_MAILING_LIST = 3,
563 H_LIST_ID = 4,
564 H_X_SEQUENCE = 5,
565 H_SENDER = 6,
566 H_LIST_POST = 7
569 FILE *fp;
571 cm_return_if_fail(msginfo != NULL);
572 cm_return_if_fail(header != NULL);
573 cm_return_if_fail(key != NULL);
575 *header = NULL;
576 *key = NULL;
578 switch (type) {
579 case FILTER_BY_NONE:
580 return;
581 case FILTER_BY_AUTO:
582 if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL)
583 return;
584 procheader_get_header_fields(fp, hentry);
585 claws_fclose(fp);
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) {
606 gchar *p;
608 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
609 p = *key;
610 while (*p != '\0') {
611 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
612 while (g_ascii_isspace(*p)) p++;
613 if (g_ascii_isdigit(*p)) {
614 *p = '\0';
615 break;
618 g_strstrip(*key);
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;
648 break;
649 case FILTER_BY_FROM:
650 *header = g_strdup("from");
651 *key = g_strdup(msginfo->from);
652 break;
653 case FILTER_BY_TO:
654 *header = g_strdup("to");
655 *key = g_strdup(msginfo->to);
656 break;
657 case FILTER_BY_SUBJECT:
658 *header = g_strdup("subject");
659 *key = g_strdup(msginfo->subject);
660 break;
661 default:
662 break;
666 static void procmsg_empty_trash(FolderItem *trash)
668 GNode *node, *next;
670 if (!trash ||
671 (trash->stype != F_TRASH &&
672 !folder_has_parent_of_type(trash, F_TRASH)))
673 return;
675 if (trash && trash->total_msgs > 0) {
676 GSList *mlist = folder_item_get_msg_list(trash);
677 GSList *cur;
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);
682 continue;
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);
690 g_slist_free(mlist);
691 folder_item_remove_all_msg(trash);
694 if (!trash->node || !trash->node->children)
695 return;
697 node = trash->node->children;
698 while (node != NULL) {
699 next = node->next;
700 procmsg_empty_trash(FOLDER_ITEM(node->data));
701 node = next;
705 void procmsg_empty_all_trash(void)
707 FolderItem *trash;
708 GList *cur;
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))
716 procmsg_empty_trash(
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;
724 FILE *fp;
725 int hnum;
726 gchar *buf = NULL;
727 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
728 {"SSV:", NULL, FALSE},
729 {"R:", 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");
749 return NULL;
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));
757 break;
759 g_free(buf);
760 buf = NULL;
762 claws_fclose(fp);
763 return mailac;
766 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
768 GSList *mia;
770 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
771 return NULL;
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;
779 return NULL;
782 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
784 MsgInfoAvatar *av;
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)
798 gchar *folder_id;
799 const gchar *msgid;
800 gchar *id;
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);
808 g_free(folder_id);
810 return id;
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);
817 const gchar *msgid;
818 FolderItem *item;
819 MsgInfo *msginfo;
821 if (separator == NULL) {
822 g_free(folder_id);
823 return NULL;
826 *separator = '\0';
827 msgid = separator + 1;
829 item = folder_find_item_from_identifier(folder_id);
831 if (item == NULL) {
832 g_free(folder_id);
833 return NULL;
836 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
837 g_free(folder_id);
839 return msginfo;
842 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
844 GSList *result = NULL;
845 GSList *orig = NULL;
846 PrefsAccount *last_account = NULL;
847 MsgInfo *msg = NULL;
848 GSList *cur = NULL;
849 gboolean nothing_to_sort = TRUE;
851 if (!list)
852 return NULL;
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);
861 debug_print("\n");
863 parse_again:
864 nothing_to_sort = TRUE;
865 cur = orig;
866 while (cur) {
867 gchar *file = NULL;
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);
872 g_free(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);
877 last_account = ac;
878 nothing_to_sort = FALSE;
879 goto parse_again;
881 cur = cur->next;
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 */
887 cur = orig;
888 while (cur) {
889 result = g_slist_append(result, cur->data);
890 cur = cur->next;
892 } else {
893 last_account = NULL;
894 goto parse_again;
898 g_slist_free(orig);
900 for (cur = result; cur; cur = cur->next)
901 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
903 debug_print("\n");
905 return result;
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);
912 GSList *cur;
913 g_free(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) {
920 g_free(file);
921 return FALSE;
925 g_free(file);
927 return TRUE;
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."));
937 if (errstr) {
938 if (*errstr) g_free(*errstr);
939 *errstr = g_strdup_printf(_("Already trying to send."));
941 return FALSE;
943 send_queue_lock = TRUE;
944 return 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;
962 GSList *list, *elem;
963 GSList *sorted_list = NULL;
964 GNode *node, *next;
966 if (!procmsg_queue_lock(errstr)) {
967 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
968 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
969 return -1;
971 inc_lock();
972 if (!queue)
973 queue = folder_get_default_queue();
975 if (queue == NULL) {
976 procmsg_queue_unlock();
977 inc_unlock();
978 return -1;
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) {
991 gchar *file;
992 MsgInfo *msginfo;
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);
997 if (file) {
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.",
1003 msginfo->msgnum);
1004 err++;
1005 } else {
1006 sent++;
1007 if (!queued_removed)
1008 folder_item_remove_msg(queue, msginfo->msgnum);
1010 g_free(file);
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) {
1025 int res = 0;
1026 next = node->next;
1027 send_queue_lock = FALSE;
1028 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1029 send_queue_lock = TRUE;
1030 if (res < 0)
1031 err = -res;
1032 else
1033 sent += res;
1034 node = next;
1037 procmsg_queue_unlock();
1038 inc_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)
1059 GSList *list;
1060 gboolean res = FALSE;
1061 if (!queue)
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);
1070 if (res == TRUE) {
1071 GNode *node, *next;
1072 if (queue->node && queue->node->children) {
1073 node = queue->node->children;
1074 while (node != NULL) {
1075 next = node->next;
1076 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1077 return FALSE;
1078 node = next;
1082 return res;
1085 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1087 FILE *fp, *outfp;
1088 gchar buf[BUFFSIZE];
1090 if ((fp = claws_fopen(in, "rb")) == NULL) {
1091 FILE_OP_ERROR(in, "claws_fopen");
1092 return -1;
1094 if ((outfp = claws_fopen(out, "wb")) == NULL) {
1095 FILE_OP_ERROR(out, "claws_fopen");
1096 claws_fclose(fp);
1097 return -1;
1099 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
1100 /* new way */
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:"))))
1105 break;
1106 /* old way */
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)) {
1113 rewind(fp);
1114 break;
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);
1121 claws_fclose(fp);
1122 return -1;
1125 claws_safe_fclose(outfp);
1126 claws_fclose(fp);
1127 return 0;
1130 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1131 gboolean is_queued)
1133 gint num;
1134 MsgInfo *msginfo, *tmp_msginfo;
1135 MsgFlags flag = {0, 0};
1136 gchar *outbox_path = NULL;
1138 if (!outbox) {
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 */
1150 if (is_queued) {
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)
1157 return -1;
1159 folder_item_scan(outbox);
1160 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1161 g_warning("can't save message");
1162 claws_unlink(tmp);
1163 return -1;
1165 } else {
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");
1170 return -1;
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-- */
1187 return 0;
1191 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1193 msginfo->refcnt++;
1195 return msginfo;
1198 MsgInfo *procmsg_msginfo_new(void)
1200 MsgInfo *newmsginfo;
1202 newmsginfo = g_new0(MsgInfo, 1);
1203 newmsginfo->refcnt = 1;
1205 return newmsginfo;
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);
1218 return newavatar;
1221 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1223 if (avatar != NULL) {
1224 if (avatar->avatar_src != NULL)
1225 g_free(avatar->avatar_src);
1226 g_free(avatar);
1230 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1232 MsgInfo *newmsginfo;
1233 GSList *refs;
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
1245 MEMBCOPY(msgnum);
1246 MEMBCOPY(size);
1247 MEMBCOPY(mtime);
1248 MEMBCOPY(date_t);
1250 MEMBCOPY(flags);
1252 MEMBDUP(fromname);
1254 MEMBDUP(date);
1255 MEMBDUP(from);
1256 MEMBDUP(to);
1257 MEMBDUP(cc);
1258 MEMBDUP(newsgroups);
1259 MEMBDUP(subject);
1260 MEMBDUP(msgid);
1261 MEMBDUP(inreplyto);
1262 MEMBDUP(xref);
1264 MEMBCOPY(folder);
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);
1294 MEMBCOPY(score);
1295 MEMBDUP(plaintext_file);
1297 return newmsginfo;
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.");
1308 return NULL;
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;
1362 gchar *file;
1364 if (msginfo == NULL) return NULL;
1366 file = procmsg_get_message_file_path(msginfo);
1367 if (!file || !is_file_exist(file)) {
1368 g_free(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.");
1373 return NULL;
1376 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1377 g_free(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;
1388 msginfo->refcnt--;
1389 if (msginfo->refcnt > 0)
1390 return;
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,
1415 NULL);
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);
1440 g_free(msginfo);
1441 *msginfo_ptr = NULL;
1443 #undef FREENULL
1445 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1447 guint memusage = 0;
1448 GSList *tmp;
1450 memusage += sizeof(MsgInfo);
1451 if (msginfo->fromname)
1452 memusage += strlen(msginfo->fromname);
1453 if (msginfo->date)
1454 memusage += strlen(msginfo->date);
1455 if (msginfo->from)
1456 memusage += strlen(msginfo->from);
1457 if (msginfo->to)
1458 memusage += strlen(msginfo->to);
1459 if (msginfo->cc)
1460 memusage += strlen(msginfo->cc);
1461 if (msginfo->newsgroups)
1462 memusage += strlen(msginfo->newsgroups);
1463 if (msginfo->subject)
1464 memusage += strlen(msginfo->subject);
1465 if (msginfo->msgid)
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);
1516 return memusage;
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}};
1541 FILE *fp;
1542 gint filepos;
1543 gint mailval = 0, newsval = 0;
1544 gchar *from = NULL;
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;
1551 gchar *buf;
1552 gint hnum;
1553 PrefsAccount *mailac = NULL, *newsac = NULL;
1554 gboolean encrypt = FALSE;
1555 FolderItem *outbox;
1557 cm_return_val_if_fail(file != NULL, -1);
1559 if ((fp = claws_fopen(file, "rb")) == NULL) {
1560 FILE_OP_ERROR(file, "claws_fopen");
1561 if (errstr) {
1562 if (*errstr) g_free(*errstr);
1563 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1565 return -1;
1568 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1569 gchar *p = buf + strlen(qentry[hnum].name);
1571 switch (hnum) {
1572 case Q_SENDER:
1573 if (from == NULL)
1574 from = g_strdup(p);
1575 break;
1576 case Q_SMTPSERVER:
1577 if (smtpserver == NULL)
1578 smtpserver = g_strdup(p);
1579 break;
1580 case Q_RECIPIENTS:
1581 to_list = address_list_append(to_list, p);
1582 break;
1583 case Q_NEWSGROUPS:
1584 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1585 break;
1586 case Q_MAIL_ACCOUNT_ID:
1587 mailac = account_find_from_id(atoi(p));
1588 break;
1589 case Q_NEWS_ACCOUNT_ID:
1590 newsac = account_find_from_id(atoi(p));
1591 break;
1592 case Q_SAVE_COPY_FOLDER:
1593 if (savecopyfolder == NULL)
1594 savecopyfolder = g_strdup(p);
1595 break;
1596 case Q_REPLY_MESSAGE_ID:
1597 if (replymessageid == NULL)
1598 replymessageid = g_strdup(p);
1599 break;
1600 case Q_FWD_MESSAGE_ID:
1601 if (fwdmessageid == NULL)
1602 fwdmessageid = g_strdup(p);
1603 break;
1604 case Q_ENCRYPT:
1605 case Q_ENCRYPT_OLD:
1606 if (p[0] == '1')
1607 encrypt = TRUE;
1608 break;
1609 case Q_CLAWS_HDRS:
1610 case Q_CLAWS_HDRS_OLD:
1611 /* end of special headers reached */
1612 g_free(buf);
1613 goto send_mail; /* can't "break;break;" */
1615 g_free(buf);
1618 send_mail:
1619 filepos = ftell(fp);
1620 if (filepos < 0) {
1621 FILE_OP_ERROR(file, "ftell");
1622 if (errstr) {
1623 if (*errstr) g_free(*errstr);
1624 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1626 return -1;
1629 if (to_list) {
1630 debug_print("Sending message by mail\n");
1631 if (!from) {
1632 if (errstr) {
1633 if (*errstr) g_free(*errstr);
1634 *errstr = g_strdup_printf(_("Queued message header is broken."));
1636 mailval = -1;
1637 } else if (mailac && mailac->use_mail_command &&
1638 mailac->mail_command && (* mailac->mail_command)) {
1639 mailval = send_message_local(mailac->mail_command, fp);
1640 } else {
1641 if (!mailac) {
1642 mailac = account_find_from_smtp_server(from, smtpserver);
1643 if (!mailac) {
1644 g_warning("Account not found. "
1645 "Using current account...");
1646 mailac = cur_account;
1650 if (mailac) {
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."));
1656 } else {
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) {
1674 if (errstr) {
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."));
1679 mailval = -1;
1682 if (fseek(fp, filepos, SEEK_SET) < 0) {
1683 FILE_OP_ERROR(file, "fseek");
1684 mailval = -1;
1687 if (newsgroup_list && newsac && (mailval == 0)) {
1688 Folder *folder;
1689 gchar *tmp = NULL;
1690 gchar buf[BUFFSIZE];
1691 FILE *tmpfp;
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");
1698 newsval = -1;
1699 alertpanel_error(_("Couldn't create temporary file for news sending."));
1700 } else {
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");
1709 newsval = -1;
1710 if (errstr) {
1711 if (*errstr) g_free(*errstr);
1712 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1716 claws_safe_fclose(tmpfp);
1718 if (newsval == 0) {
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);
1730 claws_unlink(tmp);
1732 g_free(tmp);
1735 claws_fclose(fp);
1737 /* update session statistics */
1738 if (mailval == 0 && newsval == 0) {
1739 /* update session stats */
1740 if (replymessageid)
1741 session_stats.replied++;
1742 else if (fwdmessageid)
1743 session_stats.forwarded++;
1744 else
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);
1754 if (!outbox) {
1755 gchar *id;
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);
1760 g_free(id);
1761 } else {
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);
1772 saved = TRUE;
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);
1776 saved = TRUE;
1778 procmsg_msginfo_free(&queued_mail);
1780 if (!saved) {
1781 debug_print("resaving queued mail to sent folder\n");
1782 procmsg_save_to_outbox(outbox, file, TRUE);
1787 if (replymessageid != NULL || fwdmessageid != NULL) {
1788 gchar **tokens;
1789 FolderItem *item;
1791 if (replymessageid != NULL)
1792 tokens = g_strsplit(replymessageid, "\t", 0);
1793 else
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) {
1799 MsgInfo *msginfo;
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);
1808 msginfo = NULL;
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);
1824 } else {
1825 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1827 procmsg_msginfo_free(&msginfo);
1830 g_strfreev(tokens);
1833 g_free(from);
1834 g_free(smtpserver);
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());
1849 return result;
1852 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1854 gint val;
1855 if (procmsg_queue_lock(errstr)) {
1856 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1857 procmsg_queue_unlock();
1858 return val;
1860 return -1;
1863 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1865 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1867 /* NEW flag */
1868 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1869 item->new_msgs++;
1872 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1873 item->new_msgs--;
1876 /* UNREAD flag */
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--;
1889 /* MARK flag */
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)
1943 FolderItem *item;
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)
1986 FolderItem *item;
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)
2024 FolderItem *item;
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)
2078 MsgInfo *tmp;
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,
2084 info->inreplyto);
2085 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2086 procmsg_msginfo_free(&tmp);
2087 return TRUE;
2088 } else if (tmp != NULL) {
2089 gboolean result;
2091 if (g_hash_table_lookup(parentmsgs, info)) {
2092 debug_print("loop detected: %d\n",
2093 info->msgnum);
2094 result = FALSE;
2095 } else {
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);
2101 return result;
2102 } else {
2103 return FALSE;
2105 } else
2106 return FALSE;
2110 *\brief Callback for cleaning up hash of parentmsgs
2112 static gboolean parentmsgs_hash_remove(gpointer key,
2113 gpointer value,
2114 gpointer user_data)
2116 return TRUE;
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)
2125 gboolean result;
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);
2134 return result;
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)
2150 GSList *cur;
2152 cm_return_val_if_fail(info!=NULL, children);
2153 if (info->msgid == NULL)
2154 return children;
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,
2165 children,
2166 all);
2170 return children;
2173 static GSList *procmsg_find_children (MsgInfo *info)
2175 GSList *children;
2176 GSList *all, *cur;
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));
2188 g_slist_free(all);
2190 return children;
2193 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2195 GSList *children = procmsg_find_children(info);
2196 GSList *cur;
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)) {
2200 if(newly_marked)
2201 info->folder->unreadmarked_msgs++;
2202 else
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,
2235 * FALSE otherwise
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))
2249 return TRUE;
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)) {
2255 return TRUE;
2258 return FALSE;
2261 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2262 GSList **filtered, GSList **unfiltered,
2263 gboolean do_filter)
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);
2272 *filtered = NULL;
2273 *unfiltered = NULL;
2275 if (list == NULL)
2276 return;
2278 total = g_slist_length(list);
2280 if (!do_filter) {
2281 *filtered = NULL;
2282 *unfiltered = g_slist_copy(list);
2283 return;
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");
2301 to_do = list;
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);
2326 else
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");
2351 if (fp)
2352 claws_fclose(fp);
2353 g_free(tmpfile);
2354 return NULL;
2357 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2358 claws_safe_fclose(fp);
2359 fp = NULL;
2360 tmp_msginfo = procheader_parse_file(
2361 tmpfile, flags,
2362 TRUE, FALSE);
2364 if (fp)
2365 claws_safe_fclose(fp);
2367 if (tmp_msginfo != NULL) {
2368 if (src_msginfo)
2369 tmp_msginfo->folder = src_msginfo->folder;
2370 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2371 } else {
2372 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2375 g_free(tmpfile);
2377 return tmp_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;
2413 int ret = 0;
2414 for (; cur; cur = cur->next) {
2415 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2416 ret |= func(info, list, spam);
2418 return ret;
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);
2428 else
2429 spam_folder_item = NULL;
2430 if (spam_get_folder_func != NULL)
2431 procmsg_spam_get_folder_func = spam_get_folder_func;
2432 else
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);
2444 if (item == NULL)
2445 item = folder_get_default_trash();
2446 return item;
2449 static void item_has_queued_mails(FolderItem *item, gpointer data)
2451 gboolean *result = (gboolean *)data;
2452 if (*result == TRUE)
2453 return;
2454 if (folder_has_parent_of_type(item, F_QUEUE)) {
2455 if (item->total_msgs == 0)
2456 return;
2457 else {
2458 GSList *msglist = folder_item_get_msg_list(item);
2459 GSList *cur;
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)) {
2464 *result = TRUE;
2465 break;
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);
2477 return result;
2480 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2482 gboolean *result = (gboolean *)data;
2483 if (*result == TRUE)
2484 return;
2485 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2486 *result = TRUE;
2489 gboolean procmsg_have_trashed_mails_fast (void)
2491 gboolean result = FALSE;
2492 folder_func_to_all_folders(item_has_trashed_mails, &result);
2493 return result;
2496 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2498 GSList *cur = NULL;
2499 gchar *tags = NULL;
2501 if (!msginfo)
2502 return NULL;
2504 if (msginfo->tags == NULL)
2505 return NULL;
2506 for (cur = msginfo->tags; cur; cur = cur->next) {
2507 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2508 if (!tag)
2509 continue;
2510 if (!tags)
2511 tags = g_strdup(tag);
2512 else {
2513 int olen = strlen(tags);
2514 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2515 tags = g_realloc(tags, nlen+1);
2516 if (!tags)
2517 return NULL;
2518 strcpy(tags+olen, ", ");
2519 strcpy(tags+olen+2, tag);
2520 tags[nlen]='\0';
2523 return tags;
2526 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2528 GSList changed;
2530 if (id == 0)
2531 return;
2533 if (!set) {
2534 msginfo->tags = g_slist_remove(
2535 msginfo->tags,
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);
2540 } else {
2541 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2542 msginfo->tags = g_slist_append(
2543 msginfo->tags,
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);