fix bug 4773, 'remove obsolescent AC_C_CONST'
[claws.git] / src / procmsg.c
blobbb4bff40ad9082c249ce6bb4f43ab30703c5b5ab
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 "config.h"
20 #include "defs.h"
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <math.h>
29 #include "main.h"
30 #include "utils.h"
31 #include "procmsg.h"
32 #include "procheader.h"
33 #include "send_message.h"
34 #include "procmime.h"
35 #include "statusbar.h"
36 #include "prefs_filtering.h"
37 #include "filtering.h"
38 #include "folder.h"
39 #include "prefs_common.h"
40 #include "account.h"
41 #include "alertpanel.h"
42 #include "news.h"
43 #include "hooks.h"
44 #include "msgcache.h"
45 #include "partial_download.h"
46 #include "mainwindow.h"
47 #include "summaryview.h"
48 #include "log.h"
49 #include "tags.h"
50 #include "timing.h"
51 #include "inc.h"
52 #include "privacy.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);
61 enum
63 Q_SENDER = 0,
64 Q_SMTPSERVER = 1,
65 Q_RECIPIENTS = 2,
66 Q_NEWSGROUPS = 3,
67 Q_MAIL_ACCOUNT_ID = 4,
68 Q_NEWS_ACCOUNT_ID = 5,
69 Q_SAVE_COPY_FOLDER = 6,
70 Q_REPLY_MESSAGE_ID = 7,
71 Q_FWD_MESSAGE_ID = 8,
72 Q_PRIVACY_SYSTEM = 9,
73 Q_ENCRYPT = 10,
74 Q_ENCRYPT_DATA = 11,
75 Q_CLAWS_HDRS = 12,
76 Q_PRIVACY_SYSTEM_OLD = 13,
77 Q_ENCRYPT_OLD = 14,
78 Q_ENCRYPT_DATA_OLD = 15,
79 Q_CLAWS_HDRS_OLD = 16,
82 void procmsg_msg_list_free(GSList *mlist)
84 GSList *cur;
85 MsgInfo *msginfo;
87 for (cur = mlist; cur != NULL; cur = cur->next) {
88 msginfo = (MsgInfo *)cur->data;
89 procmsg_msginfo_free(&msginfo);
91 g_slist_free(mlist);
94 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
96 GSList *cur = NULL;
97 GSList *nums = NULL;
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);
107 struct MarkSum {
108 gint *new_msgs;
109 gint *unread_msgs;
110 gint *total_msgs;
111 gint *min;
112 gint *max;
113 gint first;
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)
130 gchar *subject;
131 MsgInfo *msginfo;
132 GSList *list = NULL;
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;
140 if (subject == NULL)
141 return;
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)
152 gchar *subject;
153 GSList *list, *cur;
154 GNode *node = NULL, *hashtable_node = NULL;
155 gint prefix_length;
156 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
157 gboolean match;
159 cm_return_val_if_fail(hashtable != NULL, NULL);
161 subject = msginfo->subject;
162 if (subject == NULL)
163 return NULL;
164 prefix_length = subject_get_prefix_length(subject);
165 if (prefix_length <= 0)
166 return NULL;
167 subject += prefix_length;
169 list = g_hash_table_lookup(hashtable, subject);
170 if (list == NULL)
171 return NULL;
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;
177 match = FALSE;
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)))
184 match = TRUE;
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)
190 match = FALSE;
192 /* can add new tests for all matching
193 nodes found by subject */
195 if (match) {
196 node = hashtable_node;
197 best_msginfo = hashtable_msginfo;
201 return node;
204 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
206 g_slist_free(value);
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;
215 MsgInfo *msginfo;
216 const gchar *msgid;
217 GSList *reflist;
218 START_TIMING("");
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;
228 parent = root;
230 if (msginfo->inreplyto) {
231 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
232 if (parent == NULL) {
233 parent = root;
236 node = g_node_insert_data_before
237 (parent, parent == root ? parent->children : NULL,
238 msginfo);
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; ) {
250 next = node->next;
251 msginfo = (MsgInfo *)node->data;
252 parent = NULL;
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)
263 break;
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)) {
270 g_node_unlink(node);
271 g_node_insert_before
272 (parent, parent->children, node);
275 node = next;
278 if (prefs_common.thread_by_subject) {
279 START_TIMING("thread by subject");
280 for (node = root->children; node && node != NULL;) {
281 next = node->next;
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
287 * in the tree to
288 find the parent node */
289 if (parent != NULL) {
290 if (g_node_is_ancestor(node, parent))
291 parent = NULL;
292 if (parent == node)
293 parent = NULL;
296 if (parent) {
297 g_node_unlink(node);
298 g_node_append(parent, node);
301 node = next;
303 END_TIMING();
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);
313 END_TIMING();
314 return root;
317 gint procmsg_move_messages(GSList *mlist)
319 GSList *cur, *movelist = NULL;
320 MsgInfo *msginfo;
321 FolderItem *dest = NULL;
322 gint retval = 0;
323 gboolean finished = TRUE;
324 if (!mlist) return 0;
326 folder_item_update_freeze();
328 next_folder:
329 for (cur = mlist; cur != NULL; cur = cur->next) {
330 msginfo = (MsgInfo *)cur->data;
331 if (!msginfo->to_folder) {
332 continue;
333 } else {
334 finished = FALSE;
336 if (!dest) {
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);
341 } else {
342 continue;
344 procmsg_msginfo_set_to_folder(msginfo, NULL);
346 if (movelist) {
347 movelist = g_slist_reverse(movelist);
348 retval |= folder_item_move_msgs(dest, movelist);
349 g_slist_free(movelist);
350 movelist = NULL;
352 if (finished == FALSE) {
353 finished = TRUE;
354 dest = NULL;
355 goto next_folder;
358 folder_item_update_thaw();
359 return retval;
362 void procmsg_copy_messages(GSList *mlist)
364 GSList *cur, *copylist = NULL;
365 MsgInfo *msginfo;
366 FolderItem *dest = NULL;
367 gboolean finished = TRUE;
368 if (!mlist) return;
370 folder_item_update_freeze();
372 next_folder:
373 for (cur = mlist; cur != NULL; cur = cur->next) {
374 msginfo = (MsgInfo *)cur->data;
375 if (!msginfo->to_folder) {
376 continue;
377 } else {
378 finished = FALSE;
380 if (!dest) {
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);
385 } else {
386 continue;
388 procmsg_msginfo_set_to_folder(msginfo, NULL);
390 if (copylist) {
391 copylist = g_slist_reverse(copylist);
392 folder_item_copy_msgs(dest, copylist);
393 g_slist_free(copylist);
394 copylist = NULL;
396 if (finished == FALSE) {
397 finished = TRUE;
398 dest = NULL;
399 goto next_folder;
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);
419 if (!filename)
420 debug_print("can't fetch message %d\n", msginfo->msgnum);
422 return filename;
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,
432 headers, body);
433 if (!filename)
434 debug_print("can't fetch message %d\n", msginfo->msgnum);
436 return filename;
439 GSList *procmsg_get_message_file_list(GSList *mlist)
441 GSList *file_list = NULL;
442 MsgInfo *msginfo;
443 MsgFileInfo *fileinfo;
444 gchar *file;
446 while (mlist != NULL) {
447 msginfo = (MsgInfo *)mlist->data;
448 file = procmsg_get_message_file(msginfo);
449 if (!file) {
450 procmsg_message_file_list_free(file_list);
451 return NULL;
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);
459 mlist = mlist->next;
462 file_list = g_slist_reverse(file_list);
464 return file_list;
467 void procmsg_message_file_list_free(MsgInfoList *file_list)
469 GSList *cur;
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);
477 g_free(fileinfo);
480 g_slist_free(file_list);
483 FILE *procmsg_open_message(MsgInfo *msginfo, gboolean skip_special_headers)
485 FILE *fp;
486 gchar *file;
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)) {
494 g_free(file);
495 file = procmsg_get_message_file(msginfo);
496 if (!file)
497 return NULL;
500 if ((fp = claws_fopen(file, "rb")) == NULL) {
501 FILE_OP_ERROR(file, "claws_fopen");
502 g_free(file);
503 return NULL;
506 g_free(file);
508 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags) ||
509 skip_special_headers == TRUE) {
510 gchar buf[BUFFSIZE];
512 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
513 /* new way */
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:"))))
518 break;
519 /* old way */
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)) {
526 rewind(fp);
527 break;
532 return fp;
535 gboolean procmsg_msg_exist(MsgInfo *msginfo)
537 gboolean ret;
539 if (!msginfo) return FALSE;
541 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
543 return ret;
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}};
558 enum
560 H_X_BEENTHERE = 0,
561 H_X_ML_NAME = 1,
562 H_X_LIST = 2,
563 H_X_MAILING_LIST = 3,
564 H_LIST_ID = 4,
565 H_X_SEQUENCE = 5,
566 H_SENDER = 6,
567 H_LIST_POST = 7
570 FILE *fp;
572 cm_return_if_fail(msginfo != NULL);
573 cm_return_if_fail(header != NULL);
574 cm_return_if_fail(key != NULL);
576 *header = NULL;
577 *key = NULL;
579 switch (type) {
580 case FILTER_BY_NONE:
581 return;
582 case FILTER_BY_AUTO:
583 if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL)
584 return;
585 procheader_get_header_fields(fp, hentry);
586 claws_fclose(fp);
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) {
607 gchar *p;
609 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
610 p = *key;
611 while (*p != '\0') {
612 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
613 while (g_ascii_isspace(*p)) p++;
614 if (g_ascii_isdigit(*p)) {
615 *p = '\0';
616 break;
619 g_strstrip(*key);
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;
647 break;
648 case FILTER_BY_FROM:
649 *header = g_strdup("from");
650 *key = g_strdup(msginfo->from);
651 break;
652 case FILTER_BY_TO:
653 *header = g_strdup("to");
654 *key = g_strdup(msginfo->to);
655 break;
656 case FILTER_BY_SUBJECT:
657 *header = g_strdup("subject");
658 *key = g_strdup(msginfo->subject);
659 break;
660 case FILTER_BY_SENDER:
661 if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL)
662 return;
663 procheader_get_header_fields(fp, hentry);
664 fclose(fp);
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
683 break;
684 default:
685 break;
689 static void procmsg_empty_trash(FolderItem *trash)
691 GNode *node, *next;
693 if (!trash ||
694 (trash->stype != F_TRASH &&
695 !folder_has_parent_of_type(trash, F_TRASH)))
696 return;
698 if (trash && trash->total_msgs > 0) {
699 GSList *mlist = folder_item_get_msg_list(trash);
700 GSList *cur;
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);
705 continue;
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);
713 g_slist_free(mlist);
714 folder_item_remove_all_msg(trash);
717 if (!trash->node || !trash->node->children)
718 return;
720 node = trash->node->children;
721 while (node != NULL) {
722 next = node->next;
723 procmsg_empty_trash(FOLDER_ITEM(node->data));
724 node = next;
728 void procmsg_empty_all_trash(void)
730 FolderItem *trash;
731 GList *cur;
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))
739 procmsg_empty_trash(
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;
747 FILE *fp;
748 int hnum;
749 gchar *buf = NULL;
750 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
751 {"SSV:", NULL, FALSE},
752 {"R:", 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");
772 return NULL;
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));
780 break;
782 g_free(buf);
783 buf = NULL;
785 if (buf)
786 g_free(buf);
787 claws_fclose(fp);
788 return mailac;
791 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
793 GSList *mia;
795 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
796 return NULL;
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;
804 return NULL;
807 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
809 MsgInfoAvatar *av;
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)
823 gchar *folder_id;
824 const gchar *msgid;
825 gchar *id;
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);
833 g_free(folder_id);
835 return id;
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);
842 const gchar *msgid;
843 FolderItem *item;
844 MsgInfo *msginfo;
846 if (separator == NULL) {
847 g_free(folder_id);
848 return NULL;
851 *separator = '\0';
852 msgid = separator + 1;
854 item = folder_find_item_from_identifier(folder_id);
856 if (item == NULL) {
857 g_free(folder_id);
858 return NULL;
861 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
862 g_free(folder_id);
864 return msginfo;
867 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
869 GSList *result = NULL;
870 GSList *orig = NULL;
871 PrefsAccount *last_account = NULL;
872 MsgInfo *msg = NULL;
873 GSList *cur = NULL;
874 gboolean nothing_to_sort = TRUE;
876 if (!list)
877 return NULL;
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);
886 debug_print("\n");
888 parse_again:
889 nothing_to_sort = TRUE;
890 cur = orig;
891 while (cur) {
892 gchar *file = NULL;
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);
897 g_free(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);
902 last_account = ac;
903 nothing_to_sort = FALSE;
904 goto parse_again;
906 cur = cur->next;
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 */
912 cur = orig;
913 while (cur) {
914 result = g_slist_append(result, cur->data);
915 cur = cur->next;
917 } else {
918 last_account = NULL;
919 goto parse_again;
923 g_slist_free(orig);
925 for (cur = result; cur; cur = cur->next)
926 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
928 debug_print("\n");
930 return result;
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);
937 GSList *cur;
938 g_free(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) {
945 g_free(file);
946 return FALSE;
950 g_free(file);
952 return TRUE;
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."));
962 if (errstr) {
963 if (*errstr) g_free(*errstr);
964 *errstr = g_strdup_printf(_("Already trying to send."));
966 return FALSE;
968 send_queue_lock = TRUE;
969 return 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;
987 GSList *list, *elem;
988 GSList *sorted_list = NULL;
989 GNode *node, *next;
991 if (!procmsg_queue_lock(errstr)) {
992 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
993 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
994 return -1;
996 inc_lock();
997 if (!queue)
998 queue = folder_get_default_queue();
1000 if (queue == NULL) {
1001 procmsg_queue_unlock();
1002 inc_unlock();
1003 return -1;
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) {
1016 gchar *file;
1017 MsgInfo *msginfo;
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);
1022 if (file) {
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",
1028 msginfo->msgnum);
1029 err++;
1030 } else {
1031 sent++;
1032 if (!queued_removed)
1033 folder_item_remove_msg(queue, msginfo->msgnum);
1035 g_free(file);
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) {
1050 int res = 0;
1051 next = node->next;
1052 send_queue_lock = FALSE;
1053 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1054 send_queue_lock = TRUE;
1055 if (res < 0)
1056 err = -res;
1057 else
1058 sent += res;
1059 node = next;
1062 procmsg_queue_unlock();
1063 inc_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)
1084 GSList *list;
1085 gboolean res = FALSE;
1086 if (!queue)
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);
1095 if (res == TRUE) {
1096 GNode *node, *next;
1097 if (queue->node && queue->node->children) {
1098 node = queue->node->children;
1099 while (node != NULL) {
1100 next = node->next;
1101 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1102 return FALSE;
1103 node = next;
1107 return res;
1110 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1112 FILE *fp, *outfp;
1113 gchar buf[BUFFSIZE];
1115 if ((fp = claws_fopen(in, "rb")) == NULL) {
1116 FILE_OP_ERROR(in, "claws_fopen");
1117 return -1;
1119 if ((outfp = claws_fopen(out, "wb")) == NULL) {
1120 FILE_OP_ERROR(out, "claws_fopen");
1121 claws_fclose(fp);
1122 return -1;
1124 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
1125 /* new way */
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:"))))
1130 break;
1131 /* old way */
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)) {
1138 rewind(fp);
1139 break;
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);
1146 claws_fclose(fp);
1147 return -1;
1150 claws_safe_fclose(outfp);
1151 claws_fclose(fp);
1152 return 0;
1155 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1156 gboolean is_queued)
1158 gint num;
1159 MsgInfo *msginfo, *tmp_msginfo;
1160 MsgFlags flag = {0, 0};
1161 gchar *outbox_path = NULL;
1163 if (!outbox) {
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 */
1175 if (is_queued) {
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)
1182 return -1;
1184 folder_item_scan(outbox);
1185 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1186 g_warning("can't save message");
1187 claws_unlink(tmp);
1188 return -1;
1190 } else {
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");
1195 return -1;
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-- */
1212 return 0;
1216 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1218 msginfo->refcnt++;
1220 return msginfo;
1223 MsgInfo *procmsg_msginfo_new(void)
1225 MsgInfo *newmsginfo;
1227 newmsginfo = g_new0(MsgInfo, 1);
1228 newmsginfo->refcnt = 1;
1230 return newmsginfo;
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);
1243 return newavatar;
1246 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1248 if (avatar != NULL) {
1249 if (avatar->avatar_src != NULL)
1250 g_free(avatar->avatar_src);
1251 g_free(avatar);
1255 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1257 MsgInfo *newmsginfo;
1258 GSList *refs;
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
1270 MEMBCOPY(msgnum);
1271 MEMBCOPY(size);
1272 MEMBCOPY(mtime);
1273 MEMBCOPY(date_t);
1275 MEMBCOPY(flags);
1277 MEMBDUP(fromname);
1279 MEMBDUP(date);
1280 MEMBDUP(from);
1281 MEMBDUP(to);
1282 MEMBDUP(cc);
1283 MEMBDUP(newsgroups);
1284 MEMBDUP(subject);
1285 MEMBDUP(msgid);
1286 MEMBDUP(inreplyto);
1287 MEMBDUP(xref);
1289 MEMBCOPY(folder);
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);
1319 MEMBCOPY(score);
1320 MEMBDUP(plaintext_file);
1322 return newmsginfo;
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");
1333 return NULL;
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;
1387 gchar *file;
1389 if (msginfo == NULL) return NULL;
1391 file = procmsg_get_message_file_path(msginfo);
1392 if (!file || !is_file_exist(file)) {
1393 g_free(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");
1398 return NULL;
1401 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1402 g_free(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;
1413 msginfo->refcnt--;
1414 if (msginfo->refcnt > 0)
1415 return;
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,
1440 NULL);
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);
1465 g_free(msginfo);
1466 *msginfo_ptr = NULL;
1468 #undef FREENULL
1470 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1472 guint memusage = 0;
1473 GSList *tmp;
1475 memusage += sizeof(MsgInfo);
1476 if (msginfo->fromname)
1477 memusage += strlen(msginfo->fromname);
1478 if (msginfo->date)
1479 memusage += strlen(msginfo->date);
1480 if (msginfo->from)
1481 memusage += strlen(msginfo->from);
1482 if (msginfo->to)
1483 memusage += strlen(msginfo->to);
1484 if (msginfo->cc)
1485 memusage += strlen(msginfo->cc);
1486 if (msginfo->newsgroups)
1487 memusage += strlen(msginfo->newsgroups);
1488 if (msginfo->subject)
1489 memusage += strlen(msginfo->subject);
1490 if (msginfo->msgid)
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);
1541 return memusage;
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}};
1566 FILE *fp;
1567 gint filepos;
1568 gint mailval = 0, newsval = 0;
1569 gchar *from = NULL;
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;
1576 gchar *buf;
1577 gint hnum;
1578 PrefsAccount *mailac = NULL, *newsac = NULL;
1579 gboolean encrypt = FALSE;
1580 FolderItem *outbox;
1582 cm_return_val_if_fail(file != NULL, -1);
1584 if ((fp = claws_fopen(file, "rb")) == NULL) {
1585 FILE_OP_ERROR(file, "claws_fopen");
1586 if (errstr) {
1587 if (*errstr) g_free(*errstr);
1588 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1590 return -1;
1593 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1594 gchar *p = buf + strlen(qentry[hnum].name);
1596 switch (hnum) {
1597 case Q_SENDER:
1598 if (from == NULL)
1599 from = g_strdup(p);
1600 break;
1601 case Q_SMTPSERVER:
1602 if (smtpserver == NULL)
1603 smtpserver = g_strdup(p);
1604 break;
1605 case Q_RECIPIENTS:
1606 to_list = address_list_append(to_list, p);
1607 break;
1608 case Q_NEWSGROUPS:
1609 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1610 break;
1611 case Q_MAIL_ACCOUNT_ID:
1612 mailac = account_find_from_id(atoi(p));
1613 break;
1614 case Q_NEWS_ACCOUNT_ID:
1615 newsac = account_find_from_id(atoi(p));
1616 break;
1617 case Q_SAVE_COPY_FOLDER:
1618 if (savecopyfolder == NULL)
1619 savecopyfolder = g_strdup(p);
1620 break;
1621 case Q_REPLY_MESSAGE_ID:
1622 if (replymessageid == NULL)
1623 replymessageid = g_strdup(p);
1624 break;
1625 case Q_FWD_MESSAGE_ID:
1626 if (fwdmessageid == NULL)
1627 fwdmessageid = g_strdup(p);
1628 break;
1629 case Q_ENCRYPT:
1630 case Q_ENCRYPT_OLD:
1631 if (p[0] == '1')
1632 encrypt = TRUE;
1633 break;
1634 case Q_CLAWS_HDRS:
1635 case Q_CLAWS_HDRS_OLD:
1636 /* end of special headers reached */
1637 g_free(buf);
1638 goto send_mail; /* can't "break;break;" */
1640 g_free(buf);
1643 send_mail:
1644 filepos = ftell(fp);
1645 if (filepos < 0) {
1646 FILE_OP_ERROR(file, "ftell");
1647 if (errstr) {
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1651 return -1;
1654 if (to_list) {
1655 debug_print("Sending message by mail\n");
1656 if (!from) {
1657 if (errstr) {
1658 if (*errstr) g_free(*errstr);
1659 *errstr = g_strdup_printf(_("Queued message header is broken."));
1661 mailval = -1;
1662 } else if (mailac && mailac->use_mail_command &&
1663 mailac->mail_command && (* mailac->mail_command)) {
1664 mailval = send_message_local(mailac->mail_command, fp);
1665 } else {
1666 if (!mailac) {
1667 mailac = account_find_from_smtp_server(from, smtpserver);
1668 if (!mailac) {
1669 g_warning("account not found, "
1670 "using current account...");
1671 mailac = cur_account;
1675 if (mailac) {
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."));
1681 } else {
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) {
1699 if (errstr) {
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."));
1704 mailval = -1;
1707 if (fseek(fp, filepos, SEEK_SET) < 0) {
1708 FILE_OP_ERROR(file, "fseek");
1709 mailval = -1;
1712 if (newsgroup_list && newsac && (mailval == 0)) {
1713 Folder *folder;
1714 gchar *tmp = NULL;
1715 gchar buf[BUFFSIZE];
1716 FILE *tmpfp;
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");
1723 newsval = -1;
1724 alertpanel_error(_("Couldn't create temporary file for news sending."));
1725 } else {
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");
1734 newsval = -1;
1735 if (errstr) {
1736 if (*errstr) g_free(*errstr);
1737 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1741 claws_safe_fclose(tmpfp);
1743 if (newsval == 0) {
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);
1755 claws_unlink(tmp);
1757 g_free(tmp);
1760 claws_fclose(fp);
1762 /* update session statistics */
1763 if (mailval == 0 && newsval == 0) {
1764 /* update session stats */
1765 if (replymessageid)
1766 session_stats.replied++;
1767 else if (fwdmessageid)
1768 session_stats.forwarded++;
1769 else
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);
1779 if (!outbox) {
1780 gchar *id;
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);
1785 g_free(id);
1786 } else {
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);
1797 saved = TRUE;
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);
1801 saved = TRUE;
1803 procmsg_msginfo_free(&queued_mail);
1805 if (!saved) {
1806 debug_print("resaving queued mail to sent folder\n");
1807 procmsg_save_to_outbox(outbox, file, TRUE);
1812 if (replymessageid != NULL || fwdmessageid != NULL) {
1813 gchar **tokens;
1814 FolderItem *item;
1816 if (replymessageid != NULL)
1817 tokens = g_strsplit(replymessageid, "\t", 0);
1818 else
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) {
1824 MsgInfo *msginfo;
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);
1833 msginfo = NULL;
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);
1849 } else {
1850 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1852 procmsg_msginfo_free(&msginfo);
1855 g_strfreev(tokens);
1858 g_free(from);
1859 g_free(smtpserver);
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());
1874 return result;
1877 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1879 gint val;
1880 if (procmsg_queue_lock(errstr)) {
1881 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1882 procmsg_queue_unlock();
1883 return val;
1885 return -1;
1888 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1890 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1892 /* NEW flag */
1893 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1894 item->new_msgs++;
1897 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1898 item->new_msgs--;
1901 /* UNREAD flag */
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--;
1914 /* MARK flag */
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)
1968 FolderItem *item;
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)
2011 FolderItem *item;
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)
2049 FolderItem *item;
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)
2103 MsgInfo *tmp;
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,
2109 info->inreplyto);
2110 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2111 procmsg_msginfo_free(&tmp);
2112 return TRUE;
2113 } else if (tmp != NULL) {
2114 gboolean result;
2116 if (g_hash_table_lookup(parentmsgs, info)) {
2117 debug_print("loop detected: %d\n",
2118 info->msgnum);
2119 result = FALSE;
2120 } else {
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);
2126 return result;
2127 } else {
2128 return FALSE;
2130 } else
2131 return FALSE;
2135 *\brief Callback for cleaning up hash of parentmsgs
2137 static gboolean parentmsgs_hash_remove(gpointer key,
2138 gpointer value,
2139 gpointer user_data)
2141 return TRUE;
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)
2150 gboolean result;
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);
2159 return result;
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)
2175 GSList *cur;
2177 cm_return_val_if_fail(info!=NULL, children);
2178 if (info->msgid == NULL)
2179 return children;
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,
2190 children,
2191 all);
2195 return children;
2198 static GSList *procmsg_find_children (MsgInfo *info)
2200 GSList *children;
2201 GSList *all, *cur;
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));
2213 g_slist_free(all);
2215 return children;
2218 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2220 GSList *children = procmsg_find_children(info);
2221 GSList *cur;
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)) {
2225 if(newly_marked)
2226 info->folder->unreadmarked_msgs++;
2227 else
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,
2260 * FALSE otherwise
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))
2274 return TRUE;
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)) {
2280 return TRUE;
2283 return FALSE;
2286 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2287 GSList **filtered, GSList **unfiltered,
2288 gboolean do_filter)
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);
2297 *filtered = NULL;
2298 *unfiltered = NULL;
2300 if (list == NULL)
2301 return;
2303 total = g_slist_length(list);
2305 if (!do_filter) {
2306 *filtered = NULL;
2307 *unfiltered = g_slist_copy(list);
2308 return;
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");
2326 to_do = list;
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);
2351 else
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");
2376 if (fp)
2377 claws_fclose(fp);
2378 g_free(tmpfile);
2379 return NULL;
2382 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2383 claws_safe_fclose(fp);
2384 fp = NULL;
2385 tmp_msginfo = procheader_parse_file(
2386 tmpfile, flags,
2387 TRUE, FALSE);
2389 if (fp)
2390 claws_safe_fclose(fp);
2392 if (tmp_msginfo != NULL) {
2393 if (src_msginfo)
2394 tmp_msginfo->folder = src_msginfo->folder;
2395 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2396 } else {
2397 g_warning("procmsg_msginfo_new_from_mimeinfo(): can't generate new msginfo");
2400 g_free(tmpfile);
2402 return tmp_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;
2438 int ret = 0;
2439 for (; cur; cur = cur->next) {
2440 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2441 ret |= func(info, list, spam);
2443 return ret;
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);
2453 else
2454 spam_folder_item = NULL;
2455 if (spam_get_folder_func != NULL)
2456 procmsg_spam_get_folder_func = spam_get_folder_func;
2457 else
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);
2469 if (item == NULL)
2470 item = folder_get_default_trash();
2471 return item;
2474 static void item_has_queued_mails(FolderItem *item, gpointer data)
2476 gboolean *result = (gboolean *)data;
2477 if (*result == TRUE)
2478 return;
2479 if (folder_has_parent_of_type(item, F_QUEUE)) {
2480 if (item->total_msgs == 0)
2481 return;
2482 else {
2483 GSList *msglist = folder_item_get_msg_list(item);
2484 GSList *cur;
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)) {
2489 *result = TRUE;
2490 break;
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);
2502 return result;
2505 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2507 gboolean *result = (gboolean *)data;
2508 if (*result == TRUE)
2509 return;
2510 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2511 *result = TRUE;
2514 gboolean procmsg_have_trashed_mails_fast (void)
2516 gboolean result = FALSE;
2517 folder_func_to_all_folders(item_has_trashed_mails, &result);
2518 return result;
2521 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2523 GSList *cur = NULL;
2524 gchar *tags = NULL;
2526 if (!msginfo)
2527 return NULL;
2529 if (msginfo->tags == NULL)
2530 return NULL;
2531 for (cur = msginfo->tags; cur; cur = cur->next) {
2532 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2533 if (!tag)
2534 continue;
2535 if (!tags)
2536 tags = g_strdup(tag);
2537 else {
2538 int olen = strlen(tags);
2539 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2540 tags = g_realloc(tags, nlen+1);
2541 if (!tags)
2542 return NULL;
2543 strcpy(tags+olen, ", ");
2544 strcpy(tags+olen+2, tag);
2545 tags[nlen]='\0';
2548 return tags;
2551 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2553 GSList changed;
2555 if (id == 0)
2556 return;
2558 if (!set) {
2559 msginfo->tags = g_slist_remove(
2560 msginfo->tags,
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);
2565 } else {
2566 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2567 msginfo->tags = g_slist_append(
2568 msginfo->tags,
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);