add user specified stylesheet option
[claws.git] / src / procmsg.c
blob5bf6513dbbc5d27e04bf7d53791d179550f6cd4f
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
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>
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"
53 extern SessionStats session_stats;
55 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
56 FolderItem *queue, gint msgnum, gboolean *queued_removed);
57 static void procmsg_update_unread_children (MsgInfo *info,
58 gboolean newly_marked);
59 enum
61 Q_SENDER = 0,
62 Q_SMTPSERVER = 1,
63 Q_RECIPIENTS = 2,
64 Q_NEWSGROUPS = 3,
65 Q_MAIL_ACCOUNT_ID = 4,
66 Q_NEWS_ACCOUNT_ID = 5,
67 Q_SAVE_COPY_FOLDER = 6,
68 Q_REPLY_MESSAGE_ID = 7,
69 Q_FWD_MESSAGE_ID = 8,
70 Q_PRIVACY_SYSTEM = 9,
71 Q_ENCRYPT = 10,
72 Q_ENCRYPT_DATA = 11,
73 Q_CLAWS_HDRS = 12,
74 Q_PRIVACY_SYSTEM_OLD = 13,
75 Q_ENCRYPT_OLD = 14,
76 Q_ENCRYPT_DATA_OLD = 15,
77 Q_CLAWS_HDRS_OLD = 16,
80 void procmsg_msg_list_free(GSList *mlist)
82 GSList *cur;
83 MsgInfo *msginfo;
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
87 procmsg_msginfo_free(msginfo);
89 g_slist_free(mlist);
92 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
94 GSList *cur = NULL;
95 GSList *nums = NULL;
97 for (cur = msglist; cur; cur = cur->next) {
98 MsgInfo *msg = (MsgInfo *)cur->data;
99 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
102 return g_slist_reverse(nums);
105 struct MarkSum {
106 gint *new_msgs;
107 gint *unread_msgs;
108 gint *total_msgs;
109 gint *min;
110 gint *max;
111 gint first;
114 /* CLAWS subject threading:
116 in the first round it inserts subject lines in a
117 hashtable (subject <-> node)
119 the second round finishes the threads by attaching
120 matching subject lines to the one found in the
121 hashtable. will use the oldest node with the same
122 subject that is not more then thread_by_subject_max_age
123 days old (see subject_hashtable_lookup)
126 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
128 gchar *subject;
129 MsgInfo *msginfo;
130 GSList *list = NULL;
132 cm_return_if_fail(hashtable != NULL);
133 cm_return_if_fail(node != NULL);
134 msginfo = (MsgInfo *) node->data;
135 cm_return_if_fail(msginfo != NULL);
137 subject = msginfo->subject;
138 if (subject == NULL)
139 return;
141 subject += subject_get_prefix_length(subject);
143 list = g_hash_table_lookup(hashtable, subject);
144 list = g_slist_prepend(list, node);
145 g_hash_table_insert(hashtable, subject, list);
148 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
150 gchar *subject;
151 GSList *list, *cur;
152 GNode *node = NULL, *hashtable_node = NULL;
153 gint prefix_length;
154 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
155 gboolean match;
157 cm_return_val_if_fail(hashtable != NULL, NULL);
159 subject = msginfo->subject;
160 if (subject == NULL)
161 return NULL;
162 prefix_length = subject_get_prefix_length(subject);
163 if (prefix_length <= 0)
164 return NULL;
165 subject += prefix_length;
167 list = g_hash_table_lookup(hashtable, subject);
168 if (list == NULL)
169 return NULL;
171 /* check all nodes with the same subject to find the best parent */
172 for (cur = list; cur; cur = cur->next) {
173 hashtable_node = (GNode *)cur->data;
174 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
175 match = FALSE;
177 /* best node should be the oldest in the found nodes */
178 /* parent node must not be older then msginfo */
179 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
180 ((best_msginfo == NULL) ||
181 (best_msginfo->date_t > hashtable_msginfo->date_t)))
182 match = TRUE;
184 /* parent node must not be more then thread_by_subject_max_age
185 days older then msginfo */
186 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
187 prefs_common.thread_by_subject_max_age * 3600 * 24)
188 match = FALSE;
190 /* can add new tests for all matching
191 nodes found by subject */
193 if (match) {
194 node = hashtable_node;
195 best_msginfo = hashtable_msginfo;
199 return node;
202 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
204 g_slist_free(value);
207 /* return the reversed thread tree */
208 GNode *procmsg_get_thread_tree(GSList *mlist)
210 GNode *root, *parent, *node, *next;
211 GHashTable *msgid_table;
212 GHashTable *subject_hashtable = NULL;
213 MsgInfo *msginfo;
214 const gchar *msgid;
215 GSList *reflist;
216 START_TIMING("");
217 root = g_node_new(NULL);
218 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
220 if (prefs_common.thread_by_subject) {
221 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
224 for (; mlist != NULL; mlist = mlist->next) {
225 msginfo = (MsgInfo *)mlist->data;
226 parent = root;
228 if (msginfo->inreplyto) {
229 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
230 if (parent == NULL) {
231 parent = root;
234 node = g_node_insert_data_before
235 (parent, parent == root ? parent->children : NULL,
236 msginfo);
237 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
238 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
240 /* CLAWS: add subject to hashtable (without prefix) */
241 if (prefs_common.thread_by_subject) {
242 subject_hashtable_insert(subject_hashtable, node);
246 /* complete the unfinished threads */
247 for (node = root->children; node != NULL; ) {
248 next = node->next;
249 msginfo = (MsgInfo *)node->data;
250 parent = NULL;
252 if (msginfo->inreplyto)
253 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
255 /* try looking for the indirect parent */
256 if (!parent && msginfo->references) {
257 for (reflist = msginfo->references;
258 reflist != NULL; reflist = reflist->next)
259 if ((parent = g_hash_table_lookup
260 (msgid_table, reflist->data)) != NULL)
261 break;
264 /* node should not be the parent, and node should not
265 be an ancestor of parent (circular reference) */
266 if (parent && parent != node &&
267 !g_node_is_ancestor(node, parent)) {
268 g_node_unlink(node);
269 g_node_insert_before
270 (parent, parent->children, node);
273 node = next;
276 if (prefs_common.thread_by_subject) {
277 START_TIMING("thread by subject");
278 for (node = root->children; node && node != NULL;) {
279 next = node->next;
280 msginfo = (MsgInfo *) node->data;
282 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
284 /* the node may already be threaded by IN-REPLY-TO, so go up
285 * in the tree to
286 find the parent node */
287 if (parent != NULL) {
288 if (g_node_is_ancestor(node, parent))
289 parent = NULL;
290 if (parent == node)
291 parent = NULL;
294 if (parent) {
295 g_node_unlink(node);
296 g_node_append(parent, node);
299 node = next;
301 END_TIMING();
304 if (prefs_common.thread_by_subject)
306 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
307 g_hash_table_destroy(subject_hashtable);
310 g_hash_table_destroy(msgid_table);
311 END_TIMING();
312 return root;
315 gint procmsg_move_messages(GSList *mlist)
317 GSList *cur, *movelist = NULL;
318 MsgInfo *msginfo;
319 FolderItem *dest = NULL;
320 gint retval = 0;
321 gboolean finished = TRUE;
322 if (!mlist) return 0;
324 folder_item_update_freeze();
326 next_folder:
327 for (cur = mlist; cur != NULL; cur = cur->next) {
328 msginfo = (MsgInfo *)cur->data;
329 if (!msginfo->to_folder) {
330 continue;
331 } else {
332 finished = FALSE;
334 if (!dest) {
335 dest = msginfo->to_folder;
336 movelist = g_slist_prepend(movelist, msginfo);
337 } else if (dest == msginfo->to_folder) {
338 movelist = g_slist_prepend(movelist, msginfo);
339 } else {
340 continue;
342 procmsg_msginfo_set_to_folder(msginfo, NULL);
344 if (movelist) {
345 movelist = g_slist_reverse(movelist);
346 retval |= folder_item_move_msgs(dest, movelist);
347 g_slist_free(movelist);
348 movelist = NULL;
350 if (finished == FALSE) {
351 finished = TRUE;
352 dest = NULL;
353 goto next_folder;
356 folder_item_update_thaw();
357 return retval;
360 void procmsg_copy_messages(GSList *mlist)
362 GSList *cur, *copylist = NULL;
363 MsgInfo *msginfo;
364 FolderItem *dest = NULL;
365 gboolean finished = TRUE;
366 if (!mlist) return;
368 folder_item_update_freeze();
370 next_folder:
371 for (cur = mlist; cur != NULL; cur = cur->next) {
372 msginfo = (MsgInfo *)cur->data;
373 if (!msginfo->to_folder) {
374 continue;
375 } else {
376 finished = FALSE;
378 if (!dest) {
379 dest = msginfo->to_folder;
380 copylist = g_slist_prepend(copylist, msginfo);
381 } else if (dest == msginfo->to_folder) {
382 copylist = g_slist_prepend(copylist, msginfo);
383 } else {
384 continue;
386 procmsg_msginfo_set_to_folder(msginfo, NULL);
388 if (copylist) {
389 copylist = g_slist_reverse(copylist);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
392 copylist = NULL;
394 if (finished == FALSE) {
395 finished = TRUE;
396 dest = NULL;
397 goto next_folder;
400 folder_item_update_thaw();
403 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
405 gchar *file;
407 cm_return_val_if_fail(msginfo != NULL, NULL);
409 if (msginfo->plaintext_file)
410 file = g_strdup(msginfo->plaintext_file);
411 else {
412 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
415 return file;
418 gchar *procmsg_get_message_file(MsgInfo *msginfo)
420 gchar *filename = NULL;
422 cm_return_val_if_fail(msginfo != NULL, NULL);
424 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
425 if (!filename)
426 debug_print("can't fetch message %d\n", msginfo->msgnum);
428 return filename;
431 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
433 gchar *filename = NULL;
435 cm_return_val_if_fail(msginfo != NULL, NULL);
437 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
438 headers, body);
439 if (!filename)
440 debug_print("can't fetch message %d\n", msginfo->msgnum);
442 return filename;
445 GSList *procmsg_get_message_file_list(GSList *mlist)
447 GSList *file_list = NULL;
448 MsgInfo *msginfo;
449 MsgFileInfo *fileinfo;
450 gchar *file;
452 while (mlist != NULL) {
453 msginfo = (MsgInfo *)mlist->data;
454 file = procmsg_get_message_file(msginfo);
455 if (!file) {
456 procmsg_message_file_list_free(file_list);
457 return NULL;
459 fileinfo = g_new(MsgFileInfo, 1);
460 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
461 fileinfo->file = file;
462 fileinfo->flags = g_new(MsgFlags, 1);
463 *fileinfo->flags = msginfo->flags;
464 file_list = g_slist_prepend(file_list, fileinfo);
465 mlist = mlist->next;
468 file_list = g_slist_reverse(file_list);
470 return file_list;
473 void procmsg_message_file_list_free(MsgInfoList *file_list)
475 GSList *cur;
476 MsgFileInfo *fileinfo;
478 for (cur = file_list; cur != NULL; cur = cur->next) {
479 fileinfo = (MsgFileInfo *)cur->data;
480 procmsg_msginfo_free(fileinfo->msginfo);
481 g_free(fileinfo->file);
482 g_free(fileinfo->flags);
483 g_free(fileinfo);
486 g_slist_free(file_list);
489 FILE *procmsg_open_message(MsgInfo *msginfo)
491 FILE *fp;
492 gchar *file;
494 cm_return_val_if_fail(msginfo != NULL, NULL);
496 file = procmsg_get_message_file_path(msginfo);
497 cm_return_val_if_fail(file != NULL, NULL);
499 if (!is_file_exist(file)) {
500 g_free(file);
501 file = procmsg_get_message_file(msginfo);
502 if (!file)
503 return NULL;
506 if ((fp = g_fopen(file, "rb")) == NULL) {
507 FILE_OP_ERROR(file, "fopen");
508 g_free(file);
509 return NULL;
512 g_free(file);
514 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
515 gchar buf[BUFFSIZE];
517 while (fgets(buf, sizeof(buf), fp) != NULL) {
518 /* new way */
519 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
520 strlen("X-Claws-End-Special-Headers:"))) ||
521 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
522 strlen("X-Sylpheed-End-Special-Headers:"))))
523 break;
524 /* old way */
525 if (buf[0] == '\r' || buf[0] == '\n') break;
526 /* from other mailers */
527 if (!strncmp(buf, "Date: ", 6)
528 || !strncmp(buf, "To: ", 4)
529 || !strncmp(buf, "From: ", 6)
530 || !strncmp(buf, "Subject: ", 9)) {
531 rewind(fp);
532 break;
537 return fp;
540 gboolean procmsg_msg_exist(MsgInfo *msginfo)
542 gchar *path;
543 gboolean ret;
545 if (!msginfo) return FALSE;
547 path = folder_item_get_path(msginfo->folder);
548 change_dir(path);
549 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
550 g_free(path);
552 return ret;
555 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
556 PrefsFilterType type)
558 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
559 {"X-ML-Name:", NULL, TRUE},
560 {"X-List:", NULL, TRUE},
561 {"X-Mailing-list:", NULL, TRUE},
562 {"List-Id:", NULL, TRUE},
563 {"X-Sequence:", NULL, TRUE},
564 {"Sender:", NULL, TRUE},
565 {"List-Post:", NULL, TRUE},
566 {NULL, NULL, FALSE}};
567 enum
569 H_X_BEENTHERE = 0,
570 H_X_ML_NAME = 1,
571 H_X_LIST = 2,
572 H_X_MAILING_LIST = 3,
573 H_LIST_ID = 4,
574 H_X_SEQUENCE = 5,
575 H_SENDER = 6,
576 H_LIST_POST = 7
579 FILE *fp;
581 cm_return_if_fail(msginfo != NULL);
582 cm_return_if_fail(header != NULL);
583 cm_return_if_fail(key != NULL);
585 *header = NULL;
586 *key = NULL;
588 switch (type) {
589 case FILTER_BY_NONE:
590 return;
591 case FILTER_BY_AUTO:
592 if ((fp = procmsg_open_message(msginfo)) == NULL)
593 return;
594 procheader_get_header_fields(fp, hentry);
595 fclose(fp);
597 #define SET_FILTER_KEY(hstr, idx) \
599 *header = g_strdup(hstr); \
600 *key = hentry[idx].body; \
601 hentry[idx].body = NULL; \
604 if (hentry[H_LIST_ID].body != NULL) {
605 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
606 extract_list_id_str(*key);
607 } else if (hentry[H_X_BEENTHERE].body != NULL) {
608 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
609 } else if (hentry[H_X_ML_NAME].body != NULL) {
610 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
611 } else if (hentry[H_X_LIST].body != NULL) {
612 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
613 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
614 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
615 } else if (hentry[H_X_SEQUENCE].body != NULL) {
616 gchar *p;
618 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
619 p = *key;
620 while (*p != '\0') {
621 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
622 while (g_ascii_isspace(*p)) p++;
623 if (g_ascii_isdigit(*p)) {
624 *p = '\0';
625 break;
628 g_strstrip(*key);
629 } else if (hentry[H_SENDER].body != NULL) {
630 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
631 } else if (hentry[H_LIST_POST].body != NULL) {
632 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
633 } else if (msginfo->to) {
634 *header = g_strdup("to");
635 *key = g_strdup(msginfo->to);
636 } else if (msginfo->subject) {
637 *header = g_strdup("subject");
638 *key = g_strdup(msginfo->subject);
641 #undef SET_FILTER_KEY
643 g_free(hentry[H_X_BEENTHERE].body);
644 hentry[H_X_BEENTHERE].body = NULL;
645 g_free(hentry[H_X_ML_NAME].body);
646 hentry[H_X_ML_NAME].body = NULL;
647 g_free(hentry[H_X_LIST].body);
648 hentry[H_X_LIST].body = NULL;
649 g_free(hentry[H_X_MAILING_LIST].body);
650 hentry[H_X_MAILING_LIST].body = NULL;
651 g_free(hentry[H_LIST_ID].body);
652 hentry[H_LIST_ID].body = NULL;
653 g_free(hentry[H_SENDER].body);
654 hentry[H_SENDER].body = NULL;
655 g_free(hentry[H_LIST_POST].body);
656 hentry[H_LIST_POST].body = NULL;
658 break;
659 case FILTER_BY_FROM:
660 *header = g_strdup("from");
661 *key = g_strdup(msginfo->from);
662 break;
663 case FILTER_BY_TO:
664 *header = g_strdup("to");
665 *key = g_strdup(msginfo->to);
666 break;
667 case FILTER_BY_SUBJECT:
668 *header = g_strdup("subject");
669 *key = g_strdup(msginfo->subject);
670 break;
671 default:
672 break;
676 static void procmsg_empty_trash(FolderItem *trash)
678 GNode *node, *next;
680 if (!trash ||
681 (trash->stype != F_TRASH &&
682 !folder_has_parent_of_type(trash, F_TRASH)))
683 return;
685 if (trash && trash->total_msgs > 0) {
686 GSList *mlist = folder_item_get_msg_list(trash);
687 GSList *cur;
688 for (cur = mlist ; cur != NULL ; cur = cur->next) {
689 MsgInfo * msginfo = (MsgInfo *) cur->data;
690 if (MSG_IS_LOCKED(msginfo->flags)) {
691 procmsg_msginfo_free(msginfo);
692 continue;
694 if (msginfo->total_size != 0 &&
695 msginfo->size != (off_t)msginfo->total_size)
696 partial_mark_for_delete(msginfo);
698 procmsg_msginfo_free(msginfo);
700 g_slist_free(mlist);
701 folder_item_remove_all_msg(trash);
704 if (!trash->node || !trash->node->children)
705 return;
707 node = trash->node->children;
708 while (node != NULL) {
709 next = node->next;
710 procmsg_empty_trash(FOLDER_ITEM(node->data));
711 node = next;
715 void procmsg_empty_all_trash(void)
717 FolderItem *trash;
718 GList *cur;
720 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
721 Folder *folder = FOLDER(cur->data);
722 trash = folder->trash;
723 procmsg_empty_trash(trash);
724 if (folder->account && folder->account->set_trash_folder &&
725 folder_find_item_from_identifier(folder->account->trash_folder))
726 procmsg_empty_trash(
727 folder_find_item_from_identifier(folder->account->trash_folder));
731 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
733 PrefsAccount *mailac = NULL;
734 FILE *fp;
735 int hnum;
736 gchar buf[BUFFSIZE];
737 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
738 {"SSV:", NULL, FALSE},
739 {"R:", NULL, FALSE},
740 {"NG:", NULL, FALSE},
741 {"MAID:", NULL, FALSE},
742 {"NAID:", NULL, FALSE},
743 {"SCF:", NULL, FALSE},
744 {"RMID:", NULL, FALSE},
745 {"FMID:", NULL, FALSE},
746 {"X-Claws-Privacy-System:", NULL, FALSE},
747 {"X-Claws-Encrypt:", NULL, FALSE},
748 {"X-Claws-Encrypt-Data:", NULL, FALSE},
749 {"X-Claws-End-Special-Headers", NULL, FALSE},
750 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
751 {"X-Sylpheed-Encrypt:", NULL, FALSE},
752 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
753 {NULL, NULL, FALSE}};
755 cm_return_val_if_fail(file != NULL, NULL);
757 if ((fp = g_fopen(file, "rb")) == NULL) {
758 FILE_OP_ERROR(file, "fopen");
759 return NULL;
762 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
763 != -1) {
764 gchar *p = buf + strlen(qentry[hnum].name);
766 if (hnum == Q_MAIL_ACCOUNT_ID) {
767 mailac = account_find_from_id(atoi(p));
768 break;
771 fclose(fp);
772 return mailac;
775 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
777 GSList *mia;
779 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
780 return NULL;
782 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
783 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
784 if (avatar->avatar_id == type)
785 return avatar->avatar_src;
788 return NULL;
791 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
793 MsgInfoAvatar *av;
795 if (!msginfo->extradata)
796 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
798 av = g_new0(MsgInfoAvatar, 1);
799 av->avatar_id = type;
800 av->avatar_src = g_strdup(data);
802 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
805 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
807 gchar *folder_id;
808 const gchar *msgid;
809 gchar *id;
811 cm_return_val_if_fail(msginfo != NULL, NULL);
812 folder_id = folder_item_get_identifier(msginfo->folder);
813 msgid = msginfo->msgid;
815 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
817 g_free(folder_id);
819 return id;
822 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
824 gchar *folder_id = g_strdup(id);
825 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
826 const gchar *msgid;
827 FolderItem *item;
828 MsgInfo *msginfo;
830 if (separator == NULL) {
831 g_free(folder_id);
832 return NULL;
835 *separator = '\0';
836 msgid = separator + 1;
838 item = folder_find_item_from_identifier(folder_id);
840 if (item == NULL) {
841 g_free(folder_id);
842 return NULL;
845 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
846 g_free(folder_id);
848 return msginfo;
851 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
853 GSList *result = NULL;
854 GSList *orig = NULL;
855 PrefsAccount *last_account = NULL;
856 MsgInfo *msg = NULL;
857 GSList *cur = NULL;
858 gboolean nothing_to_sort = TRUE;
860 if (!list)
861 return NULL;
863 orig = g_slist_copy(list);
865 msg = (MsgInfo *)orig->data;
867 for (cur = orig; cur; cur = cur->next)
868 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
870 debug_print("\n");
872 parse_again:
873 nothing_to_sort = TRUE;
874 cur = orig;
875 while (cur) {
876 gchar *file = NULL;
877 PrefsAccount *ac = NULL;
878 msg = (MsgInfo *)cur->data;
879 file = folder_item_fetch_msg(queue, msg->msgnum);
880 ac = procmsg_get_account_from_file(file);
881 g_free(file);
883 if (last_account == NULL || (ac != NULL && ac == last_account)) {
884 result = g_slist_append(result, msg);
885 orig = g_slist_remove(orig, msg);
886 last_account = ac;
887 nothing_to_sort = FALSE;
888 goto parse_again;
890 cur = cur->next;
893 if (orig && g_slist_length(orig)) {
894 if (!last_account && nothing_to_sort) {
895 /* can't find an account for the rest of the list */
896 cur = orig;
897 while (cur) {
898 result = g_slist_append(result, cur->data);
899 cur = cur->next;
901 } else {
902 last_account = NULL;
903 goto parse_again;
907 g_slist_free(orig);
909 for (cur = result; cur; cur = cur->next)
910 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
912 debug_print("\n");
914 return result;
917 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
919 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
920 PrefsAccount *ac = procmsg_get_account_from_file(file);
921 GSList *cur;
922 g_free(file);
923 for (cur = elem; cur; cur = cur->next) {
924 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
925 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
927 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
928 if (procmsg_get_account_from_file(file) == ac) {
929 g_free(file);
930 return FALSE;
934 g_free(file);
936 return TRUE;
939 static gboolean send_queue_lock = FALSE;
941 gboolean procmsg_queue_lock(char **errstr)
943 if (send_queue_lock) {
944 /* Avoid having to translate two similar strings */
945 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
946 if (errstr) {
947 if (*errstr) g_free(*errstr);
948 *errstr = g_strdup_printf(_("Already trying to send."));
950 return FALSE;
952 send_queue_lock = TRUE;
953 return TRUE;
955 void procmsg_queue_unlock(void)
957 send_queue_lock = FALSE;
960 *\brief Send messages in queue
962 *\param queue Queue folder to process
963 *\param save_msgs Unused
965 *\return Number of messages sent, negative if an error occurred
966 * positive if no error occurred
968 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
970 gint sent = 0, err = 0;
971 GSList *list, *elem;
972 GSList *sorted_list = NULL;
973 GNode *node, *next;
975 if (!procmsg_queue_lock(errstr)) {
976 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
977 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
978 return -1;
980 inc_lock();
981 if (!queue)
982 queue = folder_get_default_queue();
984 if (queue == NULL) {
985 procmsg_queue_unlock();
986 inc_unlock();
987 return -1;
990 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
991 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
993 folder_item_scan(queue);
994 list = folder_item_get_msg_list(queue);
996 /* sort the list per sender account; this helps reusing the same SMTP server */
997 sorted_list = procmsg_list_sort_by_account(queue, list);
999 for (elem = sorted_list; elem != NULL; elem = elem->next) {
1000 gchar *file;
1001 MsgInfo *msginfo;
1003 msginfo = (MsgInfo *)(elem->data);
1004 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
1005 file = folder_item_fetch_msg(queue, msginfo->msgnum);
1006 if (file) {
1007 gboolean queued_removed = FALSE;
1008 if (procmsg_send_message_queue_full(file,
1009 !procmsg_is_last_for_account(queue, msginfo, elem),
1010 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1011 g_warning("Sending queued message %d failed.\n",
1012 msginfo->msgnum);
1013 err++;
1014 } else {
1015 sent++;
1016 if (!queued_removed)
1017 folder_item_remove_msg(queue, msginfo->msgnum);
1019 g_free(file);
1022 /* FIXME: supposedly if only one message is locked, and queue
1023 * is being flushed, the following free says something like
1024 * "freeing msg ## in folder (nil)". */
1025 procmsg_msginfo_free(msginfo);
1028 g_slist_free(sorted_list);
1029 folder_item_scan(queue);
1031 if (queue->node && queue->node->children) {
1032 node = queue->node->children;
1033 while (node != NULL) {
1034 int res = 0;
1035 next = node->next;
1036 send_queue_lock = FALSE;
1037 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1038 send_queue_lock = TRUE;
1039 if (res < 0)
1040 err = -res;
1041 else
1042 sent += res;
1043 node = next;
1046 procmsg_queue_unlock();
1047 inc_unlock();
1048 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1049 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1051 return (err != 0 ? -err : sent);
1054 gboolean procmsg_is_sending(void)
1056 return send_queue_lock;
1060 *\brief Determine if a queue folder is empty
1062 *\param queue Queue folder to process
1064 *\return TRUE if the queue folder is empty, otherwise return FALSE
1066 gboolean procmsg_queue_is_empty(FolderItem *queue)
1068 GSList *list;
1069 gboolean res = FALSE;
1070 if (!queue)
1071 queue = folder_get_default_queue();
1072 cm_return_val_if_fail(queue != NULL, TRUE);
1074 folder_item_scan(queue);
1075 list = folder_item_get_msg_list(queue);
1076 res = (list == NULL);
1077 procmsg_msg_list_free(list);
1079 if (res == TRUE) {
1080 GNode *node, *next;
1081 if (queue->node && queue->node->children) {
1082 node = queue->node->children;
1083 while (node != NULL) {
1084 next = node->next;
1085 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1086 return FALSE;
1087 node = next;
1091 return res;
1094 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1096 FILE *fp, *outfp;
1097 gchar buf[BUFFSIZE];
1099 if ((fp = g_fopen(in, "rb")) == NULL) {
1100 FILE_OP_ERROR(in, "fopen");
1101 return -1;
1103 if ((outfp = g_fopen(out, "wb")) == NULL) {
1104 FILE_OP_ERROR(out, "fopen");
1105 fclose(fp);
1106 return -1;
1108 while (fgets(buf, sizeof(buf), fp) != NULL) {
1109 /* new way */
1110 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1111 strlen("X-Claws-End-Special-Headers:"))) ||
1112 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1113 strlen("X-Sylpheed-End-Special-Headers:"))))
1114 break;
1115 /* old way */
1116 if (buf[0] == '\r' || buf[0] == '\n') break;
1117 /* from other mailers */
1118 if (!strncmp(buf, "Date: ", 6)
1119 || !strncmp(buf, "To: ", 4)
1120 || !strncmp(buf, "From: ", 6)
1121 || !strncmp(buf, "Subject: ", 9)) {
1122 rewind(fp);
1123 break;
1126 while (fgets(buf, sizeof(buf), fp) != NULL) {
1127 if (fputs(buf, outfp) == EOF) {
1128 FILE_OP_ERROR(out, "fputs");
1129 fclose(outfp);
1130 fclose(fp);
1131 return -1;
1134 fclose(outfp);
1135 fclose(fp);
1136 return 0;
1139 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1140 gboolean is_queued)
1142 gint num;
1143 MsgInfo *msginfo, *tmp_msginfo;
1144 MsgFlags flag = {0, 0};
1146 debug_print("saving sent message...\n");
1148 if (!outbox)
1149 outbox = folder_get_default_outbox();
1150 cm_return_val_if_fail(outbox != NULL, -1);
1152 /* remove queueing headers */
1153 if (is_queued) {
1154 gchar tmp[MAXPATHLEN + 1];
1156 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1157 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1159 if (procmsg_remove_special_headers(file, tmp) !=0)
1160 return -1;
1162 folder_item_scan(outbox);
1163 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1164 g_warning("can't save message\n");
1165 claws_unlink(tmp);
1166 return -1;
1168 } else {
1169 folder_item_scan(outbox);
1170 if ((num = folder_item_add_msg
1171 (outbox, file, &flag, FALSE)) < 0) {
1172 g_warning("can't save message\n");
1173 return -1;
1176 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1177 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1178 if (msginfo != NULL) {
1179 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1180 procmsg_msginfo_free(msginfo); /* refcnt-- */
1181 /* tmp_msginfo == msginfo */
1182 if (tmp_msginfo && msginfo->extradata &&
1183 (msginfo->extradata->dispositionnotificationto ||
1184 msginfo->extradata->returnreceiptto)) {
1185 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1187 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1190 return 0;
1194 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1196 msginfo->refcnt++;
1198 return msginfo;
1201 MsgInfo *procmsg_msginfo_new(void)
1203 MsgInfo *newmsginfo;
1205 newmsginfo = g_new0(MsgInfo, 1);
1206 newmsginfo->refcnt = 1;
1208 return newmsginfo;
1211 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1213 MsgInfoAvatar *newavatar;
1215 if (avatar == NULL) return NULL;
1217 newavatar = g_new0(MsgInfoAvatar, 1);
1218 newavatar->avatar_id = avatar->avatar_id;
1219 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1221 return newavatar;
1224 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1226 if (avatar != NULL) {
1227 if (avatar->avatar_src != NULL)
1228 g_free(avatar->avatar_src);
1229 g_free(avatar);
1233 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1235 MsgInfo *newmsginfo;
1236 GSList *refs;
1238 if (msginfo == NULL) return NULL;
1240 newmsginfo = g_new0(MsgInfo, 1);
1242 newmsginfo->refcnt = 1;
1244 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1245 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1246 g_strdup(msginfo->mmb) : NULL
1248 MEMBCOPY(msgnum);
1249 MEMBCOPY(size);
1250 MEMBCOPY(mtime);
1251 MEMBCOPY(date_t);
1253 MEMBCOPY(flags);
1255 MEMBDUP(fromname);
1257 MEMBDUP(date);
1258 MEMBDUP(from);
1259 MEMBDUP(to);
1260 MEMBDUP(cc);
1261 MEMBDUP(newsgroups);
1262 MEMBDUP(subject);
1263 MEMBDUP(msgid);
1264 MEMBDUP(inreplyto);
1265 MEMBDUP(xref);
1267 MEMBCOPY(folder);
1268 MEMBCOPY(to_folder);
1270 if (msginfo->extradata) {
1271 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1272 if (msginfo->extradata->avatars) {
1273 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1274 (GCopyFunc) procmsg_msginfoavatar_copy);
1276 MEMBDUP(extradata->dispositionnotificationto);
1277 MEMBDUP(extradata->returnreceiptto);
1278 MEMBDUP(extradata->partial_recv);
1279 MEMBDUP(extradata->account_server);
1280 MEMBDUP(extradata->account_login);
1281 MEMBDUP(extradata->list_post);
1282 MEMBDUP(extradata->list_subscribe);
1283 MEMBDUP(extradata->list_unsubscribe);
1284 MEMBDUP(extradata->list_help);
1285 MEMBDUP(extradata->list_archive);
1286 MEMBDUP(extradata->list_owner);
1287 MEMBDUP(extradata->resent_from);
1290 refs = msginfo->references;
1291 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1292 newmsginfo->references = g_slist_prepend
1293 (newmsginfo->references, g_strdup(refs->data));
1295 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1297 MEMBCOPY(score);
1298 MEMBDUP(plaintext_file);
1300 return newmsginfo;
1303 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1305 MsgInfo *full_msginfo;
1307 if (msginfo == NULL) return NULL;
1309 if (!file || !is_file_exist(file)) {
1310 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1311 return NULL;
1314 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1315 if (!full_msginfo) return NULL;
1317 msginfo->total_size = full_msginfo->total_size;
1318 msginfo->planned_download = full_msginfo->planned_download;
1320 if (full_msginfo->extradata) {
1321 if (!msginfo->extradata)
1322 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1323 if (!msginfo->extradata->list_post)
1324 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1325 if (!msginfo->extradata->list_subscribe)
1326 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1327 if (!msginfo->extradata->list_unsubscribe)
1328 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1329 if (!msginfo->extradata->list_help)
1330 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1331 if (!msginfo->extradata->list_archive)
1332 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1333 if (!msginfo->extradata->list_owner)
1334 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1335 if (!msginfo->extradata->avatars)
1336 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1337 (GCopyFunc) procmsg_msginfoavatar_copy);
1338 if (!msginfo->extradata->dispositionnotificationto)
1339 msginfo->extradata->dispositionnotificationto =
1340 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1341 if (!msginfo->extradata->returnreceiptto)
1342 msginfo->extradata->returnreceiptto = g_strdup
1343 (full_msginfo->extradata->returnreceiptto);
1344 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1345 msginfo->extradata->partial_recv = g_strdup
1346 (full_msginfo->extradata->partial_recv);
1347 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1348 msginfo->extradata->account_server = g_strdup
1349 (full_msginfo->extradata->account_server);
1350 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1351 msginfo->extradata->account_login = g_strdup
1352 (full_msginfo->extradata->account_login);
1353 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1354 msginfo->extradata->resent_from = g_strdup
1355 (full_msginfo->extradata->resent_from);
1357 procmsg_msginfo_free(full_msginfo);
1359 return procmsg_msginfo_new_ref(msginfo);
1362 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1364 MsgInfo *full_msginfo;
1365 gchar *file;
1367 if (msginfo == NULL) return NULL;
1369 file = procmsg_get_message_file_path(msginfo);
1370 if (!file || !is_file_exist(file)) {
1371 g_free(file);
1372 file = procmsg_get_message_file(msginfo);
1374 if (!file || !is_file_exist(file)) {
1375 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1376 return NULL;
1379 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1380 g_free(file);
1381 return full_msginfo;
1384 void procmsg_msginfo_free(MsgInfo *msginfo)
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 g_free(msginfo->fromspace);
1399 g_free(msginfo->fromname);
1401 g_free(msginfo->date);
1402 g_free(msginfo->from);
1403 g_free(msginfo->to);
1404 g_free(msginfo->cc);
1405 g_free(msginfo->newsgroups);
1406 g_free(msginfo->subject);
1407 g_free(msginfo->msgid);
1408 g_free(msginfo->inreplyto);
1409 g_free(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);
1418 g_free(msginfo->extradata->returnreceiptto);
1419 g_free(msginfo->extradata->dispositionnotificationto);
1420 g_free(msginfo->extradata->list_post);
1421 g_free(msginfo->extradata->list_subscribe);
1422 g_free(msginfo->extradata->list_unsubscribe);
1423 g_free(msginfo->extradata->list_help);
1424 g_free(msginfo->extradata->list_archive);
1425 g_free(msginfo->extradata->list_owner);
1426 g_free(msginfo->extradata->partial_recv);
1427 g_free(msginfo->extradata->account_server);
1428 g_free(msginfo->extradata->account_login);
1429 g_free(msginfo->extradata->resent_from);
1430 g_free(msginfo->extradata);
1432 slist_free_strings_full(msginfo->references);
1433 g_slist_free(msginfo->tags);
1435 g_free(msginfo->plaintext_file);
1437 g_free(msginfo);
1440 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1442 guint memusage = 0;
1443 GSList *tmp;
1445 memusage += sizeof(MsgInfo);
1446 if (msginfo->fromname)
1447 memusage += strlen(msginfo->fromname);
1448 if (msginfo->date)
1449 memusage += strlen(msginfo->date);
1450 if (msginfo->from)
1451 memusage += strlen(msginfo->from);
1452 if (msginfo->to)
1453 memusage += strlen(msginfo->to);
1454 if (msginfo->cc)
1455 memusage += strlen(msginfo->cc);
1456 if (msginfo->newsgroups)
1457 memusage += strlen(msginfo->newsgroups);
1458 if (msginfo->subject)
1459 memusage += strlen(msginfo->subject);
1460 if (msginfo->msgid)
1461 memusage += strlen(msginfo->msgid);
1462 if (msginfo->inreplyto)
1463 memusage += strlen(msginfo->inreplyto);
1465 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1466 gchar *r = (gchar *)tmp->data;
1467 memusage += r?strlen(r):0 + sizeof(GSList);
1469 if (msginfo->fromspace)
1470 memusage += strlen(msginfo->fromspace);
1472 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1473 memusage += sizeof(GSList);
1475 if (msginfo->extradata) {
1476 memusage += sizeof(MsgInfoExtraData);
1477 if (msginfo->extradata->avatars) {
1478 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1479 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1480 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1481 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1484 if (msginfo->extradata->dispositionnotificationto)
1485 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1486 if (msginfo->extradata->returnreceiptto)
1487 memusage += strlen(msginfo->extradata->returnreceiptto);
1489 if (msginfo->extradata->partial_recv)
1490 memusage += strlen(msginfo->extradata->partial_recv);
1491 if (msginfo->extradata->account_server)
1492 memusage += strlen(msginfo->extradata->account_server);
1493 if (msginfo->extradata->account_login)
1494 memusage += strlen(msginfo->extradata->account_login);
1495 if (msginfo->extradata->resent_from)
1496 memusage += strlen(msginfo->extradata->resent_from);
1498 if (msginfo->extradata->list_post)
1499 memusage += strlen(msginfo->extradata->list_post);
1500 if (msginfo->extradata->list_subscribe)
1501 memusage += strlen(msginfo->extradata->list_subscribe);
1502 if (msginfo->extradata->list_unsubscribe)
1503 memusage += strlen(msginfo->extradata->list_unsubscribe);
1504 if (msginfo->extradata->list_help)
1505 memusage += strlen(msginfo->extradata->list_help);
1506 if (msginfo->extradata->list_archive)
1507 memusage += strlen(msginfo->extradata->list_archive);
1508 if (msginfo->extradata->list_owner)
1509 memusage += strlen(msginfo->extradata->list_owner);
1511 return memusage;
1514 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1515 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1517 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1518 {"SSV:", NULL, FALSE},
1519 {"R:", NULL, FALSE},
1520 {"NG:", NULL, FALSE},
1521 {"MAID:", NULL, FALSE},
1522 {"NAID:", NULL, FALSE},
1523 {"SCF:", NULL, FALSE},
1524 {"RMID:", NULL, FALSE},
1525 {"FMID:", NULL, FALSE},
1526 {"X-Claws-Privacy-System:", NULL, FALSE},
1527 {"X-Claws-Encrypt:", NULL, FALSE},
1528 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1529 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1530 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1531 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1532 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1533 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1534 {NULL, NULL, FALSE}};
1535 FILE *fp;
1536 gint filepos;
1537 gint mailval = 0, newsval = 0;
1538 gchar *from = NULL;
1539 gchar *smtpserver = NULL;
1540 GSList *to_list = NULL;
1541 GSList *newsgroup_list = NULL;
1542 gchar *savecopyfolder = NULL;
1543 gchar *replymessageid = NULL;
1544 gchar *fwdmessageid = NULL;
1545 gchar *privacy_system = NULL;
1546 gboolean encrypt = FALSE;
1547 gchar *encrypt_data = NULL;
1548 gchar buf[BUFFSIZE];
1549 gint hnum;
1550 PrefsAccount *mailac = NULL, *newsac = NULL;
1551 gboolean save_clear_text = TRUE;
1552 gchar *tmp_enc_file = NULL;
1554 cm_return_val_if_fail(file != NULL, -1);
1556 if ((fp = g_fopen(file, "rb")) == NULL) {
1557 FILE_OP_ERROR(file, "fopen");
1558 if (errstr) {
1559 if (*errstr) g_free(*errstr);
1560 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1562 return -1;
1565 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1566 != -1) {
1567 gchar *p = buf + strlen(qentry[hnum].name);
1569 switch (hnum) {
1570 case Q_SENDER:
1571 if (from == NULL)
1572 from = g_strdup(p);
1573 break;
1574 case Q_SMTPSERVER:
1575 if (smtpserver == NULL)
1576 smtpserver = g_strdup(p);
1577 break;
1578 case Q_RECIPIENTS:
1579 to_list = address_list_append(to_list, p);
1580 break;
1581 case Q_NEWSGROUPS:
1582 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1583 break;
1584 case Q_MAIL_ACCOUNT_ID:
1585 mailac = account_find_from_id(atoi(p));
1586 break;
1587 case Q_NEWS_ACCOUNT_ID:
1588 newsac = account_find_from_id(atoi(p));
1589 break;
1590 case Q_SAVE_COPY_FOLDER:
1591 if (savecopyfolder == NULL)
1592 savecopyfolder = g_strdup(p);
1593 break;
1594 case Q_REPLY_MESSAGE_ID:
1595 if (replymessageid == NULL)
1596 replymessageid = g_strdup(p);
1597 break;
1598 case Q_FWD_MESSAGE_ID:
1599 if (fwdmessageid == NULL)
1600 fwdmessageid = g_strdup(p);
1601 break;
1602 case Q_PRIVACY_SYSTEM:
1603 case Q_PRIVACY_SYSTEM_OLD:
1604 if (privacy_system == NULL)
1605 privacy_system = g_strdup(p);
1606 break;
1607 case Q_ENCRYPT:
1608 case Q_ENCRYPT_OLD:
1609 if (p[0] == '1')
1610 encrypt = TRUE;
1611 break;
1612 case Q_ENCRYPT_DATA:
1613 case Q_ENCRYPT_DATA_OLD:
1614 if (encrypt_data == NULL)
1615 encrypt_data = g_strdup(p);
1616 break;
1617 case Q_CLAWS_HDRS:
1618 case Q_CLAWS_HDRS_OLD:
1619 /* end of special headers reached */
1620 goto send_mail; /* can't "break;break;" */
1623 send_mail:
1624 filepos = ftell(fp);
1625 if (filepos < 0) {
1626 FILE_OP_ERROR(file, "ftell");
1627 if (errstr) {
1628 if (*errstr) g_free(*errstr);
1629 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1631 return -1;
1634 if (encrypt) {
1635 MimeInfo *mimeinfo;
1637 if (mailac && mailac->save_encrypted_as_clear_text
1638 && !mailac->encrypt_to_self)
1639 save_clear_text = TRUE;
1640 else
1641 save_clear_text = FALSE;
1643 fclose(fp);
1644 fp = NULL;
1646 mimeinfo = procmime_scan_queue_file(file);
1647 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1648 || (fp = my_tmpfile()) == NULL
1649 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1650 if (fp)
1651 fclose(fp);
1652 procmime_mimeinfo_free_all(mimeinfo);
1653 g_free(from);
1654 g_free(smtpserver);
1655 slist_free_strings_full(to_list);
1656 slist_free_strings_full(newsgroup_list);
1657 g_free(savecopyfolder);
1658 g_free(replymessageid);
1659 g_free(fwdmessageid);
1660 g_free(privacy_system);
1661 g_free(encrypt_data);
1662 if (errstr) {
1663 if (*errstr) g_free(*errstr);
1664 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1665 privacy_get_error());
1667 return -1;
1670 rewind(fp);
1671 if (!save_clear_text) {
1672 gchar *content = NULL;
1673 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1674 if (tmpfp) {
1675 fclose(tmpfp);
1677 content = file_read_stream_to_str(fp);
1678 rewind(fp);
1680 str_write_to_file(content, tmp_enc_file);
1681 g_free(content);
1682 } else {
1683 g_warning("couldn't get tempfile\n");
1687 procmime_mimeinfo_free_all(mimeinfo);
1689 filepos = 0;
1692 if (to_list) {
1693 debug_print("Sending message by mail\n");
1694 if (!from) {
1695 if (errstr) {
1696 if (*errstr) g_free(*errstr);
1697 *errstr = g_strdup_printf(_("Queued message header is broken."));
1699 mailval = -1;
1700 } else if (mailac && mailac->use_mail_command &&
1701 mailac->mail_command && (* mailac->mail_command)) {
1702 mailval = send_message_local(mailac->mail_command, fp);
1703 } else {
1704 if (!mailac) {
1705 mailac = account_find_from_smtp_server(from, smtpserver);
1706 if (!mailac) {
1707 g_warning("Account not found. "
1708 "Using current account...\n");
1709 mailac = cur_account;
1713 if (mailac) {
1714 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1715 if (mailval == -1 && errstr) {
1716 if (*errstr) g_free(*errstr);
1717 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1719 } else {
1720 PrefsAccount tmp_ac;
1722 g_warning("Account not found.\n");
1724 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1725 tmp_ac.address = from;
1726 tmp_ac.smtp_server = smtpserver;
1727 tmp_ac.smtpport = SMTP_PORT;
1728 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1729 if (mailval == -1 && errstr) {
1730 if (*errstr) g_free(*errstr);
1731 *errstr = g_strdup_printf(_("No specific account has been found to "
1732 "send, and an error happened during SMTP session."));
1736 } else if (!to_list && !newsgroup_list) {
1737 if (errstr) {
1738 if (*errstr) g_free(*errstr);
1739 *errstr = g_strdup(_("Couldn't determine sending information. "
1740 "Maybe the email hasn't been generated by Claws Mail."));
1742 mailval = -1;
1745 if (fseek(fp, filepos, SEEK_SET) < 0) {
1746 FILE_OP_ERROR(file, "fseek");
1747 mailval = -1;
1750 if (newsgroup_list && newsac && (mailval == 0)) {
1751 Folder *folder;
1752 gchar *tmp = NULL;
1753 FILE *tmpfp;
1755 /* write to temporary file */
1756 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1757 G_DIR_SEPARATOR, file);
1758 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1759 FILE_OP_ERROR(tmp, "fopen");
1760 newsval = -1;
1761 alertpanel_error(_("Couldn't create temporary file for news sending."));
1762 } else {
1763 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1764 FILE_OP_ERROR(tmp, "chmod");
1765 g_warning("can't change file mode\n");
1768 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1769 if (fputs(buf, tmpfp) == EOF) {
1770 FILE_OP_ERROR(tmp, "fputs");
1771 newsval = -1;
1772 if (errstr) {
1773 if (*errstr) g_free(*errstr);
1774 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1778 fclose(tmpfp);
1780 if (newsval == 0) {
1781 debug_print("Sending message by news\n");
1783 folder = FOLDER(newsac->folder);
1785 newsval = news_post(folder, tmp);
1786 if (newsval < 0 && errstr) {
1787 if (*errstr) g_free(*errstr);
1788 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1789 newsac->nntp_server);
1792 claws_unlink(tmp);
1794 g_free(tmp);
1797 fclose(fp);
1799 /* update session statistics */
1800 if (mailval == 0 && newsval == 0) {
1801 /* update session stats */
1802 if (replymessageid)
1803 session_stats.replied++;
1804 else if (fwdmessageid)
1805 session_stats.forwarded++;
1806 else
1807 session_stats.sent++;
1810 /* save message to outbox */
1811 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1812 FolderItem *outbox;
1814 debug_print("saving sent message...\n");
1816 outbox = folder_find_item_from_identifier(savecopyfolder);
1817 if (!outbox)
1818 outbox = folder_get_default_outbox();
1820 if (save_clear_text || tmp_enc_file == NULL) {
1821 gboolean saved = FALSE;
1822 *queued_removed = FALSE;
1823 if (queue && msgnum > 0) {
1824 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1825 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1826 debug_print("moved queued mail %d to sent folder\n", msgnum);
1827 saved = TRUE;
1828 *queued_removed = TRUE;
1829 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1830 debug_print("copied queued mail %d to sent folder\n", msgnum);
1831 saved = TRUE;
1833 procmsg_msginfo_free(queued_mail);
1835 if (!saved) {
1836 debug_print("resaving clear text queued mail to sent folder\n");
1837 procmsg_save_to_outbox(outbox, file, TRUE);
1839 } else {
1840 debug_print("saving encrpyted queued mail to sent folder\n");
1841 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1845 if (tmp_enc_file != NULL) {
1846 claws_unlink(tmp_enc_file);
1847 free(tmp_enc_file);
1848 tmp_enc_file = NULL;
1851 if (replymessageid != NULL || fwdmessageid != NULL) {
1852 gchar **tokens;
1853 FolderItem *item;
1855 if (replymessageid != NULL)
1856 tokens = g_strsplit(replymessageid, "\t", 0);
1857 else
1858 tokens = g_strsplit(fwdmessageid, "\t", 0);
1859 item = folder_find_item_from_identifier(tokens[0]);
1861 /* check if queued message has valid folder and message id */
1862 if (item != NULL && tokens[2] != NULL) {
1863 MsgInfo *msginfo;
1865 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1867 /* check if referring message exists and has a message id */
1868 if ((msginfo != NULL) &&
1869 (msginfo->msgid != NULL) &&
1870 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1871 procmsg_msginfo_free(msginfo);
1872 msginfo = NULL;
1875 if (msginfo == NULL) {
1876 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1879 if (msginfo != NULL) {
1880 if (replymessageid != NULL) {
1881 MsgPermFlags to_unset = 0;
1883 if (prefs_common.mark_as_read_on_new_window)
1884 to_unset = (MSG_NEW|MSG_UNREAD);
1886 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1887 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1888 } else {
1889 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1891 procmsg_msginfo_free(msginfo);
1894 g_strfreev(tokens);
1897 g_free(from);
1898 g_free(smtpserver);
1899 slist_free_strings_full(to_list);
1900 slist_free_strings_full(newsgroup_list);
1901 g_free(savecopyfolder);
1902 g_free(replymessageid);
1903 g_free(fwdmessageid);
1904 g_free(privacy_system);
1905 g_free(encrypt_data);
1907 return (newsval != 0 ? newsval : mailval);
1910 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1912 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1913 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1914 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1915 return result;
1918 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1920 gint val;
1921 if (procmsg_queue_lock(errstr)) {
1922 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1923 procmsg_queue_unlock();
1924 return val;
1926 return -1;
1929 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1931 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1933 /* NEW flag */
1934 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1935 item->new_msgs++;
1938 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1939 item->new_msgs--;
1942 /* UNREAD flag */
1943 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1944 item->unread_msgs++;
1945 if (procmsg_msg_has_marked_parent(msginfo))
1946 item->unreadmarked_msgs++;
1949 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1950 item->unread_msgs--;
1951 if (procmsg_msg_has_marked_parent(msginfo))
1952 item->unreadmarked_msgs--;
1955 /* MARK flag */
1956 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1957 procmsg_update_unread_children(msginfo, TRUE);
1958 item->marked_msgs++;
1961 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1962 procmsg_update_unread_children(msginfo, FALSE);
1963 item->marked_msgs--;
1966 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1967 item->replied_msgs++;
1970 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1971 item->replied_msgs--;
1974 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1975 item->forwarded_msgs++;
1978 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1979 item->forwarded_msgs--;
1982 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1983 item->locked_msgs++;
1986 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1987 item->locked_msgs--;
1990 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1991 item->ignored_msgs--;
1994 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1995 item->ignored_msgs++;
1998 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1999 item->watched_msgs--;
2002 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2003 item->watched_msgs++;
2007 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2009 FolderItem *item;
2010 MsgInfoUpdate msginfo_update;
2011 MsgPermFlags perm_flags_new, perm_flags_old;
2012 MsgTmpFlags tmp_flags_old;
2014 cm_return_if_fail(msginfo != NULL);
2015 item = msginfo->folder;
2016 cm_return_if_fail(item != NULL);
2018 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2020 /* Perm Flags handling */
2021 perm_flags_old = msginfo->flags.perm_flags;
2022 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2023 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2024 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2026 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2027 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2030 if (perm_flags_old != perm_flags_new) {
2031 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2033 update_folder_msg_counts(item, msginfo, perm_flags_old);
2034 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2037 /* Tmp flags handling */
2038 tmp_flags_old = msginfo->flags.tmp_flags;
2039 msginfo->flags.tmp_flags |= tmp_flags;
2041 /* update notification */
2042 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2043 msginfo_update.msginfo = msginfo;
2044 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2045 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2046 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2050 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2052 FolderItem *item;
2053 MsgInfoUpdate msginfo_update;
2054 MsgPermFlags perm_flags_new, perm_flags_old;
2055 MsgTmpFlags tmp_flags_old;
2057 cm_return_if_fail(msginfo != NULL);
2058 item = msginfo->folder;
2059 cm_return_if_fail(item != NULL);
2061 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2063 /* Perm Flags handling */
2064 perm_flags_old = msginfo->flags.perm_flags;
2065 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2067 if (perm_flags_old != perm_flags_new) {
2068 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2070 update_folder_msg_counts(item, msginfo, perm_flags_old);
2073 /* Tmp flags hanlding */
2074 tmp_flags_old = msginfo->flags.tmp_flags;
2075 msginfo->flags.tmp_flags &= ~tmp_flags;
2077 /* update notification */
2078 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2079 msginfo_update.msginfo = msginfo;
2080 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2081 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2082 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2086 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2087 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2088 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2090 FolderItem *item;
2091 MsgInfoUpdate msginfo_update;
2092 MsgPermFlags perm_flags_new, perm_flags_old;
2093 MsgTmpFlags tmp_flags_old;
2095 cm_return_if_fail(msginfo != NULL);
2096 item = msginfo->folder;
2097 cm_return_if_fail(item != NULL);
2099 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2101 /* Perm Flags handling */
2102 perm_flags_old = msginfo->flags.perm_flags;
2103 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2104 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2105 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2107 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2108 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2111 if (perm_flags_old != perm_flags_new) {
2112 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2114 update_folder_msg_counts(item, msginfo, perm_flags_old);
2118 /* Tmp flags handling */
2119 tmp_flags_old = msginfo->flags.tmp_flags;
2120 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2121 msginfo->flags.tmp_flags |= add_tmp_flags;
2123 /* update notification */
2124 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2125 msginfo_update.msginfo = msginfo;
2126 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2127 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2128 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2133 *\brief check for flags (e.g. mark) in prior msgs of current thread
2135 *\param info Current message
2136 *\param perm_flags Flags to be checked
2137 *\param parentmsgs Hash of prior msgs to avoid loops
2139 *\return gboolean TRUE if perm_flags are found
2141 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2142 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2144 MsgInfo *tmp;
2146 cm_return_val_if_fail(info != NULL, FALSE);
2148 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2149 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2150 info->inreplyto);
2151 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2152 procmsg_msginfo_free(tmp);
2153 return TRUE;
2154 } else if (tmp != NULL) {
2155 gboolean result;
2157 if (g_hash_table_lookup(parentmsgs, info)) {
2158 debug_print("loop detected: %d\n",
2159 info->msgnum);
2160 result = FALSE;
2161 } else {
2162 g_hash_table_insert(parentmsgs, info, "1");
2163 result = procmsg_msg_has_flagged_parent_real(
2164 tmp, perm_flags, parentmsgs);
2166 procmsg_msginfo_free(tmp);
2167 return result;
2168 } else {
2169 return FALSE;
2171 } else
2172 return FALSE;
2176 *\brief Callback for cleaning up hash of parentmsgs
2178 static gboolean parentmsgs_hash_remove(gpointer key,
2179 gpointer value,
2180 gpointer user_data)
2182 return TRUE;
2186 *\brief Set up list of parentmsgs
2187 * See procmsg_msg_has_flagged_parent_real()
2189 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2191 gboolean result;
2192 static GHashTable *parentmsgs = NULL;
2194 if (parentmsgs == NULL)
2195 parentmsgs = g_hash_table_new(NULL, NULL);
2197 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2198 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2200 return result;
2204 *\brief Check if msgs prior in thread are marked
2205 * See procmsg_msg_has_flagged_parent_real()
2207 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2209 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2213 static GSList *procmsg_find_children_func(MsgInfo *info,
2214 GSList *children, GSList *all)
2216 GSList *cur;
2218 cm_return_val_if_fail(info!=NULL, children);
2219 if (info->msgid == NULL)
2220 return children;
2222 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2223 MsgInfo *tmp = (MsgInfo *)cur->data;
2224 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2225 /* Check if message is already in the list */
2226 if ((children == NULL) ||
2227 (g_slist_index(children, tmp) == -1)) {
2228 children = g_slist_prepend(children,
2229 procmsg_msginfo_new_ref(tmp));
2230 children = procmsg_find_children_func(tmp,
2231 children,
2232 all);
2236 return children;
2239 static GSList *procmsg_find_children (MsgInfo *info)
2241 GSList *children;
2242 GSList *all, *cur;
2244 cm_return_val_if_fail(info!=NULL, NULL);
2245 all = folder_item_get_msg_list(info->folder);
2246 children = procmsg_find_children_func(info, NULL, all);
2247 if (children != NULL) {
2248 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2249 /* this will not free the used pointers
2250 created with procmsg_msginfo_new_ref */
2251 procmsg_msginfo_free((MsgInfo *)cur->data);
2254 g_slist_free(all);
2256 return children;
2259 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2261 GSList *children = procmsg_find_children(info);
2262 GSList *cur;
2263 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2264 MsgInfo *tmp = (MsgInfo *)cur->data;
2265 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2266 if(newly_marked)
2267 info->folder->unreadmarked_msgs++;
2268 else
2269 info->folder->unreadmarked_msgs--;
2270 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2272 procmsg_msginfo_free(tmp);
2274 g_slist_free(children);
2278 * Set the destination folder for a copy or move operation
2280 * \param msginfo The message which's destination folder is changed
2281 * \param to_folder The destination folder for the operation
2283 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2285 if(msginfo->to_folder != NULL) {
2286 msginfo->to_folder->op_count--;
2287 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2289 msginfo->to_folder = to_folder;
2290 if(to_folder != NULL) {
2291 to_folder->op_count++;
2292 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2297 * Apply filtering actions to the msginfo
2299 * \param msginfo The MsgInfo describing the message that should be filtered
2300 * \return TRUE if the message was moved and MsgInfo is now invalid,
2301 * FALSE otherwise
2303 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2305 MailFilteringData mail_filtering_data;
2307 mail_filtering_data.msginfo = msginfo;
2308 mail_filtering_data.msglist = NULL;
2309 mail_filtering_data.filtered = NULL;
2310 mail_filtering_data.unfiltered = NULL;
2311 mail_filtering_data.account = ac_prefs;
2313 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2314 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2315 return TRUE;
2317 /* filter if enabled in prefs or move to inbox if not */
2318 if((filtering_rules != NULL) &&
2319 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2320 FILTERING_INCORPORATION, NULL)) {
2321 return TRUE;
2324 return FALSE;
2327 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2328 GSList **filtered, GSList **unfiltered,
2329 gboolean do_filter)
2331 GSList *cur, *to_do = NULL;
2332 gint total = 0, curnum = 0;
2333 MailFilteringData mail_filtering_data;
2335 cm_return_if_fail(filtered != NULL);
2336 cm_return_if_fail(unfiltered != NULL);
2338 *filtered = NULL;
2339 *unfiltered = NULL;
2341 if (list == NULL)
2342 return;
2344 total = g_slist_length(list);
2346 if (!do_filter) {
2347 *filtered = NULL;
2348 *unfiltered = g_slist_copy(list);
2349 return;
2352 statusbar_print_all(_("Filtering messages...\n"));
2354 mail_filtering_data.msginfo = NULL;
2355 mail_filtering_data.msglist = list;
2356 mail_filtering_data.filtered = NULL;
2357 mail_filtering_data.unfiltered = NULL;
2358 mail_filtering_data.account = ac;
2360 if (!ac || ac->filterhook_on_recv)
2361 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2363 if (mail_filtering_data.filtered == NULL &&
2364 mail_filtering_data.unfiltered == NULL) {
2365 /* nothing happened */
2366 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2367 to_do = list;
2369 if (mail_filtering_data.filtered != NULL) {
2370 /* keep track of what's been filtered by the hooks */
2371 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2372 g_slist_length(list),
2373 g_slist_length(mail_filtering_data.filtered),
2374 g_slist_length(mail_filtering_data.unfiltered));
2376 *filtered = g_slist_copy(mail_filtering_data.filtered);
2378 if (mail_filtering_data.unfiltered != NULL) {
2379 /* what the hooks didn't handle will go in filtered or
2380 * unfiltered in the next loop */
2381 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2382 g_slist_length(list),
2383 g_slist_length(mail_filtering_data.filtered),
2384 g_slist_length(mail_filtering_data.unfiltered));
2385 to_do = mail_filtering_data.unfiltered;
2388 for (cur = to_do; cur; cur = cur->next) {
2389 MsgInfo *info = (MsgInfo *)cur->data;
2390 if (procmsg_msginfo_filter(info, ac))
2391 *filtered = g_slist_prepend(*filtered, info);
2392 else
2393 *unfiltered = g_slist_prepend(*unfiltered, info);
2394 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2397 g_slist_free(mail_filtering_data.filtered);
2398 g_slist_free(mail_filtering_data.unfiltered);
2400 *filtered = g_slist_reverse(*filtered);
2401 *unfiltered = g_slist_reverse(*unfiltered);
2403 statusbar_progress_all(0,0,0);
2404 statusbar_pop_all();
2407 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2409 MsgInfo *tmp_msginfo = NULL;
2410 MsgFlags flags = {0, 0};
2411 gchar *tmpfile = get_tmp_file();
2412 FILE *fp = g_fopen(tmpfile, "wb");
2414 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2415 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2416 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2417 if (fp)
2418 fclose(fp);
2419 g_free(tmpfile);
2420 return NULL;
2423 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2424 fclose(fp);
2425 fp = NULL;
2426 tmp_msginfo = procheader_parse_file(
2427 tmpfile, flags,
2428 TRUE, FALSE);
2430 if (fp)
2431 fclose(fp);
2433 if (tmp_msginfo != NULL) {
2434 if (src_msginfo)
2435 tmp_msginfo->folder = src_msginfo->folder;
2436 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2437 } else {
2438 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2441 g_free(tmpfile);
2443 return tmp_msginfo;
2446 static GSList *spam_learners = NULL;
2448 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2450 if (!g_slist_find(spam_learners, learn_func))
2451 spam_learners = g_slist_append(spam_learners, learn_func);
2452 if (mainwindow_get_mainwindow()) {
2453 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2454 summary_set_menu_sensitive(
2455 mainwindow_get_mainwindow()->summaryview);
2456 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2460 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2462 spam_learners = g_slist_remove(spam_learners, learn_func);
2463 if (mainwindow_get_mainwindow()) {
2464 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2465 summary_set_menu_sensitive(
2466 mainwindow_get_mainwindow()->summaryview);
2467 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2471 gboolean procmsg_spam_can_learn(void)
2473 return g_slist_length(spam_learners) > 0;
2476 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2478 GSList *cur = spam_learners;
2479 int ret = 0;
2480 for (; cur; cur = cur->next) {
2481 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2482 ret |= func(info, list, spam);
2484 return ret;
2487 static gchar *spam_folder_item = NULL;
2488 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2489 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2491 g_free(spam_folder_item);
2492 if (item_identifier)
2493 spam_folder_item = g_strdup(item_identifier);
2494 else
2495 spam_folder_item = NULL;
2496 if (spam_get_folder_func != NULL)
2497 procmsg_spam_get_folder_func = spam_get_folder_func;
2498 else
2499 procmsg_spam_get_folder_func = NULL;
2502 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2504 FolderItem *item = NULL;
2506 if (procmsg_spam_get_folder_func)
2507 item = procmsg_spam_get_folder_func(msginfo);
2508 if (item == NULL && spam_folder_item)
2509 item = folder_find_item_from_identifier(spam_folder_item);
2510 if (item == NULL)
2511 item = folder_get_default_trash();
2512 return item;
2515 static void item_has_queued_mails(FolderItem *item, gpointer data)
2517 gboolean *result = (gboolean *)data;
2518 if (*result == TRUE)
2519 return;
2520 if (folder_has_parent_of_type(item, F_QUEUE)) {
2521 if (item->total_msgs == 0)
2522 return;
2523 else {
2524 GSList *msglist = folder_item_get_msg_list(item);
2525 GSList *cur;
2526 for (cur = msglist; cur; cur = cur->next) {
2527 MsgInfo *msginfo = (MsgInfo *)cur->data;
2528 if (!MSG_IS_DELETED(msginfo->flags) &&
2529 !MSG_IS_LOCKED(msginfo->flags)) {
2530 *result = TRUE;
2531 break;
2534 procmsg_msg_list_free(msglist);
2539 gboolean procmsg_have_queued_mails_fast (void)
2541 gboolean result = FALSE;
2542 folder_func_to_all_folders(item_has_queued_mails, &result);
2543 return result;
2546 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2548 gboolean *result = (gboolean *)data;
2549 if (*result == TRUE)
2550 return;
2551 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2552 *result = TRUE;
2555 gboolean procmsg_have_trashed_mails_fast (void)
2557 gboolean result = FALSE;
2558 folder_func_to_all_folders(item_has_trashed_mails, &result);
2559 return result;
2562 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2564 GSList *cur = NULL;
2565 gchar *tags = NULL;
2567 if (!msginfo)
2568 return NULL;
2570 if (msginfo->tags == NULL)
2571 return NULL;
2572 for (cur = msginfo->tags; cur; cur = cur->next) {
2573 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2574 if (!tag)
2575 continue;
2576 if (!tags)
2577 tags = g_strdup(tag);
2578 else {
2579 int olen = strlen(tags);
2580 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2581 tags = g_realloc(tags, nlen+1);
2582 if (!tags)
2583 return NULL;
2584 strcpy(tags+olen, ", ");
2585 strcpy(tags+olen+2, tag);
2586 tags[nlen]='\0';
2589 return tags;
2592 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2594 GSList changed;
2596 if (id == 0)
2597 return;
2599 if (!set) {
2600 msginfo->tags = g_slist_remove(
2601 msginfo->tags,
2602 GINT_TO_POINTER(id));
2603 changed.data = GINT_TO_POINTER(id);
2604 changed.next = NULL;
2605 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2606 } else {
2607 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2608 msginfo->tags = g_slist_append(
2609 msginfo->tags,
2610 GINT_TO_POINTER(id));
2612 changed.data = GINT_TO_POINTER(id);
2613 changed.next = NULL;
2614 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2619 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2621 GSList *unset = msginfo->tags;
2622 msginfo->tags = NULL;
2623 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2624 g_slist_free(unset);