Fix bug #3574: Template addressing
[claws.git] / src / procmsg.c
blobaa3c9a829bcce192867cf69cdb2c068a1a06c140
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 cm_return_val_if_fail(msginfo != NULL, NULL);
407 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
410 gchar *procmsg_get_message_file(MsgInfo *msginfo)
412 gchar *filename = NULL;
414 cm_return_val_if_fail(msginfo != NULL, NULL);
416 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
417 if (!filename)
418 debug_print("can't fetch message %d\n", msginfo->msgnum);
420 return filename;
423 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
425 gchar *filename = NULL;
427 cm_return_val_if_fail(msginfo != NULL, NULL);
429 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
430 headers, body);
431 if (!filename)
432 debug_print("can't fetch message %d\n", msginfo->msgnum);
434 return filename;
437 GSList *procmsg_get_message_file_list(GSList *mlist)
439 GSList *file_list = NULL;
440 MsgInfo *msginfo;
441 MsgFileInfo *fileinfo;
442 gchar *file;
444 while (mlist != NULL) {
445 msginfo = (MsgInfo *)mlist->data;
446 file = procmsg_get_message_file(msginfo);
447 if (!file) {
448 procmsg_message_file_list_free(file_list);
449 return NULL;
451 fileinfo = g_new(MsgFileInfo, 1);
452 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
453 fileinfo->file = file;
454 fileinfo->flags = g_new(MsgFlags, 1);
455 *fileinfo->flags = msginfo->flags;
456 file_list = g_slist_prepend(file_list, fileinfo);
457 mlist = mlist->next;
460 file_list = g_slist_reverse(file_list);
462 return file_list;
465 void procmsg_message_file_list_free(MsgInfoList *file_list)
467 GSList *cur;
468 MsgFileInfo *fileinfo;
470 for (cur = file_list; cur != NULL; cur = cur->next) {
471 fileinfo = (MsgFileInfo *)cur->data;
472 procmsg_msginfo_free(&(fileinfo->msginfo));
473 g_free(fileinfo->file);
474 g_free(fileinfo->flags);
475 g_free(fileinfo);
478 g_slist_free(file_list);
481 FILE *procmsg_open_message(MsgInfo *msginfo)
483 FILE *fp;
484 gchar *file;
486 cm_return_val_if_fail(msginfo != NULL, NULL);
488 file = procmsg_get_message_file_path(msginfo);
489 cm_return_val_if_fail(file != NULL, NULL);
491 if (!is_file_exist(file)) {
492 g_free(file);
493 file = procmsg_get_message_file(msginfo);
494 if (!file)
495 return NULL;
498 if ((fp = g_fopen(file, "rb")) == NULL) {
499 FILE_OP_ERROR(file, "fopen");
500 g_free(file);
501 return NULL;
504 g_free(file);
506 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
507 gchar buf[BUFFSIZE];
509 while (fgets(buf, sizeof(buf), fp) != NULL) {
510 /* new way */
511 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
512 strlen("X-Claws-End-Special-Headers:"))) ||
513 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
514 strlen("X-Sylpheed-End-Special-Headers:"))))
515 break;
516 /* old way */
517 if (buf[0] == '\r' || buf[0] == '\n') break;
518 /* from other mailers */
519 if (!strncmp(buf, "Date: ", 6)
520 || !strncmp(buf, "To: ", 4)
521 || !strncmp(buf, "From: ", 6)
522 || !strncmp(buf, "Subject: ", 9)) {
523 rewind(fp);
524 break;
529 return fp;
532 gboolean procmsg_msg_exist(MsgInfo *msginfo)
534 gchar *path;
535 gboolean ret;
537 if (!msginfo) return FALSE;
539 path = folder_item_get_path(msginfo->folder);
540 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
541 g_free(path);
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)) == NULL)
584 return;
585 procheader_get_header_fields(fp, hentry);
586 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 #undef SET_FILTER_KEY
634 g_free(hentry[H_X_BEENTHERE].body);
635 hentry[H_X_BEENTHERE].body = NULL;
636 g_free(hentry[H_X_ML_NAME].body);
637 hentry[H_X_ML_NAME].body = NULL;
638 g_free(hentry[H_X_LIST].body);
639 hentry[H_X_LIST].body = NULL;
640 g_free(hentry[H_X_MAILING_LIST].body);
641 hentry[H_X_MAILING_LIST].body = NULL;
642 g_free(hentry[H_LIST_ID].body);
643 hentry[H_LIST_ID].body = NULL;
644 g_free(hentry[H_SENDER].body);
645 hentry[H_SENDER].body = NULL;
646 g_free(hentry[H_LIST_POST].body);
647 hentry[H_LIST_POST].body = NULL;
649 break;
650 case FILTER_BY_FROM:
651 *header = g_strdup("from");
652 *key = g_strdup(msginfo->from);
653 break;
654 case FILTER_BY_TO:
655 *header = g_strdup("to");
656 *key = g_strdup(msginfo->to);
657 break;
658 case FILTER_BY_SUBJECT:
659 *header = g_strdup("subject");
660 *key = g_strdup(msginfo->subject);
661 break;
662 default:
663 break;
667 static void procmsg_empty_trash(FolderItem *trash)
669 GNode *node, *next;
671 if (!trash ||
672 (trash->stype != F_TRASH &&
673 !folder_has_parent_of_type(trash, F_TRASH)))
674 return;
676 if (trash && trash->total_msgs > 0) {
677 GSList *mlist = folder_item_get_msg_list(trash);
678 GSList *cur;
679 for (cur = mlist ; cur != NULL ; cur = cur->next) {
680 MsgInfo * msginfo = (MsgInfo *) cur->data;
681 if (MSG_IS_LOCKED(msginfo->flags)) {
682 procmsg_msginfo_free(&msginfo);
683 continue;
685 if (msginfo->total_size != 0 &&
686 msginfo->size != (off_t)msginfo->total_size)
687 partial_mark_for_delete(msginfo);
689 procmsg_msginfo_free(&msginfo);
691 g_slist_free(mlist);
692 folder_item_remove_all_msg(trash);
695 if (!trash->node || !trash->node->children)
696 return;
698 node = trash->node->children;
699 while (node != NULL) {
700 next = node->next;
701 procmsg_empty_trash(FOLDER_ITEM(node->data));
702 node = next;
706 void procmsg_empty_all_trash(void)
708 FolderItem *trash;
709 GList *cur;
711 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
712 Folder *folder = FOLDER(cur->data);
713 trash = folder->trash;
714 procmsg_empty_trash(trash);
715 if (folder->account && folder->account->set_trash_folder &&
716 folder_find_item_from_identifier(folder->account->trash_folder))
717 procmsg_empty_trash(
718 folder_find_item_from_identifier(folder->account->trash_folder));
722 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
724 PrefsAccount *mailac = NULL;
725 FILE *fp;
726 int hnum;
727 gchar buf[BUFFSIZE];
728 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
729 {"SSV:", NULL, FALSE},
730 {"R:", NULL, FALSE},
731 {"NG:", NULL, FALSE},
732 {"MAID:", NULL, FALSE},
733 {"NAID:", NULL, FALSE},
734 {"SCF:", NULL, FALSE},
735 {"RMID:", NULL, FALSE},
736 {"FMID:", NULL, FALSE},
737 {"X-Claws-Privacy-System:", NULL, FALSE},
738 {"X-Claws-Encrypt:", NULL, FALSE},
739 {"X-Claws-Encrypt-Data:", NULL, FALSE},
740 {"X-Claws-End-Special-Headers", NULL, FALSE},
741 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
742 {"X-Sylpheed-Encrypt:", NULL, FALSE},
743 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
744 {NULL, NULL, FALSE}};
746 cm_return_val_if_fail(file != NULL, NULL);
748 if ((fp = g_fopen(file, "rb")) == NULL) {
749 FILE_OP_ERROR(file, "fopen");
750 return NULL;
753 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
754 != -1) {
755 gchar *p = buf + strlen(qentry[hnum].name);
757 if (hnum == Q_MAIL_ACCOUNT_ID) {
758 mailac = account_find_from_id(atoi(p));
759 break;
762 fclose(fp);
763 return mailac;
766 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
768 GSList *mia;
770 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
771 return NULL;
773 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
774 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
775 if (avatar->avatar_id == type)
776 return avatar->avatar_src;
779 return NULL;
782 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
784 MsgInfoAvatar *av;
786 if (!msginfo->extradata)
787 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
789 av = g_new0(MsgInfoAvatar, 1);
790 av->avatar_id = type;
791 av->avatar_src = g_strdup(data);
793 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
796 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
798 gchar *folder_id;
799 const gchar *msgid;
800 gchar *id;
802 cm_return_val_if_fail(msginfo != NULL, NULL);
803 folder_id = folder_item_get_identifier(msginfo->folder);
804 msgid = msginfo->msgid;
806 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
808 g_free(folder_id);
810 return id;
813 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
815 gchar *folder_id = g_strdup(id);
816 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
817 const gchar *msgid;
818 FolderItem *item;
819 MsgInfo *msginfo;
821 if (separator == NULL) {
822 g_free(folder_id);
823 return NULL;
826 *separator = '\0';
827 msgid = separator + 1;
829 item = folder_find_item_from_identifier(folder_id);
831 if (item == NULL) {
832 g_free(folder_id);
833 return NULL;
836 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
837 g_free(folder_id);
839 return msginfo;
842 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
844 GSList *result = NULL;
845 GSList *orig = NULL;
846 PrefsAccount *last_account = NULL;
847 MsgInfo *msg = NULL;
848 GSList *cur = NULL;
849 gboolean nothing_to_sort = TRUE;
851 if (!list)
852 return NULL;
854 orig = g_slist_copy(list);
856 msg = (MsgInfo *)orig->data;
858 for (cur = orig; cur; cur = cur->next)
859 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
861 debug_print("\n");
863 parse_again:
864 nothing_to_sort = TRUE;
865 cur = orig;
866 while (cur) {
867 gchar *file = NULL;
868 PrefsAccount *ac = NULL;
869 msg = (MsgInfo *)cur->data;
870 file = folder_item_fetch_msg(queue, msg->msgnum);
871 ac = procmsg_get_account_from_file(file);
872 g_free(file);
874 if (last_account == NULL || (ac != NULL && ac == last_account)) {
875 result = g_slist_append(result, msg);
876 orig = g_slist_remove(orig, msg);
877 last_account = ac;
878 nothing_to_sort = FALSE;
879 goto parse_again;
881 cur = cur->next;
884 if (orig && g_slist_length(orig)) {
885 if (!last_account && nothing_to_sort) {
886 /* can't find an account for the rest of the list */
887 cur = orig;
888 while (cur) {
889 result = g_slist_append(result, cur->data);
890 cur = cur->next;
892 } else {
893 last_account = NULL;
894 goto parse_again;
898 g_slist_free(orig);
900 for (cur = result; cur; cur = cur->next)
901 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
903 debug_print("\n");
905 return result;
908 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
910 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
911 PrefsAccount *ac = procmsg_get_account_from_file(file);
912 GSList *cur;
913 g_free(file);
914 for (cur = elem; cur; cur = cur->next) {
915 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
916 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
918 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
919 if (procmsg_get_account_from_file(file) == ac) {
920 g_free(file);
921 return FALSE;
925 g_free(file);
927 return TRUE;
930 static gboolean send_queue_lock = FALSE;
932 gboolean procmsg_queue_lock(char **errstr)
934 if (send_queue_lock) {
935 /* Avoid having to translate two similar strings */
936 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
937 if (errstr) {
938 if (*errstr) g_free(*errstr);
939 *errstr = g_strdup_printf(_("Already trying to send."));
941 return FALSE;
943 send_queue_lock = TRUE;
944 return TRUE;
946 void procmsg_queue_unlock(void)
948 send_queue_lock = FALSE;
951 *\brief Send messages in queue
953 *\param queue Queue folder to process
954 *\param save_msgs Unused
956 *\return Number of messages sent, negative if an error occurred
957 * positive if no error occurred
959 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
961 gint sent = 0, err = 0;
962 GSList *list, *elem;
963 GSList *sorted_list = NULL;
964 GNode *node, *next;
966 if (!procmsg_queue_lock(errstr)) {
967 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
968 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
969 return -1;
971 inc_lock();
972 if (!queue)
973 queue = folder_get_default_queue();
975 if (queue == NULL) {
976 procmsg_queue_unlock();
977 inc_unlock();
978 return -1;
981 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
982 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
984 folder_item_scan(queue);
985 list = folder_item_get_msg_list(queue);
987 /* sort the list per sender account; this helps reusing the same SMTP server */
988 sorted_list = procmsg_list_sort_by_account(queue, list);
990 for (elem = sorted_list; elem != NULL; elem = elem->next) {
991 gchar *file;
992 MsgInfo *msginfo;
994 msginfo = (MsgInfo *)(elem->data);
995 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
996 file = folder_item_fetch_msg(queue, msginfo->msgnum);
997 if (file) {
998 gboolean queued_removed = FALSE;
999 if (procmsg_send_message_queue_full(file,
1000 !procmsg_is_last_for_account(queue, msginfo, elem),
1001 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1002 g_warning("Sending queued message %d failed.",
1003 msginfo->msgnum);
1004 err++;
1005 } else {
1006 sent++;
1007 if (!queued_removed)
1008 folder_item_remove_msg(queue, msginfo->msgnum);
1010 g_free(file);
1013 /* FIXME: supposedly if only one message is locked, and queue
1014 * is being flushed, the following free says something like
1015 * "freeing msg ## in folder (nil)". */
1016 procmsg_msginfo_free(&msginfo);
1019 g_slist_free(sorted_list);
1020 folder_item_scan(queue);
1022 if (queue->node && queue->node->children) {
1023 node = queue->node->children;
1024 while (node != NULL) {
1025 int res = 0;
1026 next = node->next;
1027 send_queue_lock = FALSE;
1028 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1029 send_queue_lock = TRUE;
1030 if (res < 0)
1031 err = -res;
1032 else
1033 sent += res;
1034 node = next;
1037 procmsg_queue_unlock();
1038 inc_unlock();
1039 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1040 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1042 return (err != 0 ? -err : sent);
1045 gboolean procmsg_is_sending(void)
1047 return send_queue_lock;
1051 *\brief Determine if a queue folder is empty
1053 *\param queue Queue folder to process
1055 *\return TRUE if the queue folder is empty, otherwise return FALSE
1057 gboolean procmsg_queue_is_empty(FolderItem *queue)
1059 GSList *list;
1060 gboolean res = FALSE;
1061 if (!queue)
1062 queue = folder_get_default_queue();
1063 cm_return_val_if_fail(queue != NULL, TRUE);
1065 folder_item_scan(queue);
1066 list = folder_item_get_msg_list(queue);
1067 res = (list == NULL);
1068 procmsg_msg_list_free(list);
1070 if (res == TRUE) {
1071 GNode *node, *next;
1072 if (queue->node && queue->node->children) {
1073 node = queue->node->children;
1074 while (node != NULL) {
1075 next = node->next;
1076 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1077 return FALSE;
1078 node = next;
1082 return res;
1085 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1087 FILE *fp, *outfp;
1088 gchar buf[BUFFSIZE];
1090 if ((fp = g_fopen(in, "rb")) == NULL) {
1091 FILE_OP_ERROR(in, "fopen");
1092 return -1;
1094 if ((outfp = g_fopen(out, "wb")) == NULL) {
1095 FILE_OP_ERROR(out, "fopen");
1096 fclose(fp);
1097 return -1;
1099 while (fgets(buf, sizeof(buf), fp) != NULL) {
1100 /* new way */
1101 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1102 strlen("X-Claws-End-Special-Headers:"))) ||
1103 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1104 strlen("X-Sylpheed-End-Special-Headers:"))))
1105 break;
1106 /* old way */
1107 if (buf[0] == '\r' || buf[0] == '\n') break;
1108 /* from other mailers */
1109 if (!strncmp(buf, "Date: ", 6)
1110 || !strncmp(buf, "To: ", 4)
1111 || !strncmp(buf, "From: ", 6)
1112 || !strncmp(buf, "Subject: ", 9)) {
1113 rewind(fp);
1114 break;
1117 while (fgets(buf, sizeof(buf), fp) != NULL) {
1118 if (fputs(buf, outfp) == EOF) {
1119 FILE_OP_ERROR(out, "fputs");
1120 fclose(outfp);
1121 fclose(fp);
1122 return -1;
1125 fclose(outfp);
1126 fclose(fp);
1127 return 0;
1130 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1131 gboolean is_queued)
1133 gint num;
1134 MsgInfo *msginfo, *tmp_msginfo;
1135 MsgFlags flag = {0, 0};
1137 debug_print("saving sent message...\n");
1139 if (!outbox)
1140 outbox = folder_get_default_outbox();
1141 cm_return_val_if_fail(outbox != NULL, -1);
1143 /* remove queueing headers */
1144 if (is_queued) {
1145 gchar tmp[MAXPATHLEN + 1];
1147 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1148 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1150 if (procmsg_remove_special_headers(file, tmp) !=0)
1151 return -1;
1153 folder_item_scan(outbox);
1154 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1155 g_warning("can't save message");
1156 claws_unlink(tmp);
1157 return -1;
1159 } else {
1160 folder_item_scan(outbox);
1161 if ((num = folder_item_add_msg
1162 (outbox, file, &flag, FALSE)) < 0) {
1163 g_warning("can't save message");
1164 return -1;
1167 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1168 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1169 if (msginfo != NULL) {
1170 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1171 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1172 /* tmp_msginfo == msginfo */
1173 if (tmp_msginfo && msginfo->extradata &&
1174 (msginfo->extradata->dispositionnotificationto ||
1175 msginfo->extradata->returnreceiptto)) {
1176 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1178 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1181 return 0;
1185 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1187 msginfo->refcnt++;
1189 return msginfo;
1192 MsgInfo *procmsg_msginfo_new(void)
1194 MsgInfo *newmsginfo;
1196 newmsginfo = g_new0(MsgInfo, 1);
1197 newmsginfo->refcnt = 1;
1199 return newmsginfo;
1202 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1204 MsgInfoAvatar *newavatar;
1206 if (avatar == NULL) return NULL;
1208 newavatar = g_new0(MsgInfoAvatar, 1);
1209 newavatar->avatar_id = avatar->avatar_id;
1210 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1212 return newavatar;
1215 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1217 if (avatar != NULL) {
1218 if (avatar->avatar_src != NULL)
1219 g_free(avatar->avatar_src);
1220 g_free(avatar);
1224 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1226 MsgInfo *newmsginfo;
1227 GSList *refs;
1229 if (msginfo == NULL) return NULL;
1231 newmsginfo = g_new0(MsgInfo, 1);
1233 newmsginfo->refcnt = 1;
1235 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1236 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1237 g_strdup(msginfo->mmb) : NULL
1239 MEMBCOPY(msgnum);
1240 MEMBCOPY(size);
1241 MEMBCOPY(mtime);
1242 MEMBCOPY(date_t);
1244 MEMBCOPY(flags);
1246 MEMBDUP(fromname);
1248 MEMBDUP(date);
1249 MEMBDUP(from);
1250 MEMBDUP(to);
1251 MEMBDUP(cc);
1252 MEMBDUP(newsgroups);
1253 MEMBDUP(subject);
1254 MEMBDUP(msgid);
1255 MEMBDUP(inreplyto);
1256 MEMBDUP(xref);
1258 MEMBCOPY(folder);
1259 MEMBCOPY(to_folder);
1261 if (msginfo->extradata) {
1262 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1263 if (msginfo->extradata->avatars) {
1264 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1265 (GCopyFunc) procmsg_msginfoavatar_copy);
1267 MEMBDUP(extradata->dispositionnotificationto);
1268 MEMBDUP(extradata->returnreceiptto);
1269 MEMBDUP(extradata->partial_recv);
1270 MEMBDUP(extradata->account_server);
1271 MEMBDUP(extradata->account_login);
1272 MEMBDUP(extradata->list_post);
1273 MEMBDUP(extradata->list_subscribe);
1274 MEMBDUP(extradata->list_unsubscribe);
1275 MEMBDUP(extradata->list_help);
1276 MEMBDUP(extradata->list_archive);
1277 MEMBDUP(extradata->list_owner);
1278 MEMBDUP(extradata->resent_from);
1281 refs = msginfo->references;
1282 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1283 newmsginfo->references = g_slist_prepend
1284 (newmsginfo->references, g_strdup(refs->data));
1286 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1288 MEMBCOPY(score);
1289 MEMBDUP(plaintext_file);
1291 return newmsginfo;
1294 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1296 MsgInfo *full_msginfo;
1298 if (msginfo == NULL) return NULL;
1300 if (!file || !is_file_exist(file)) {
1301 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1302 return NULL;
1305 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1306 if (!full_msginfo) return NULL;
1308 msginfo->total_size = full_msginfo->total_size;
1309 msginfo->planned_download = full_msginfo->planned_download;
1311 if (full_msginfo->extradata) {
1312 if (!msginfo->extradata)
1313 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1314 if (!msginfo->extradata->list_post)
1315 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1316 if (!msginfo->extradata->list_subscribe)
1317 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1318 if (!msginfo->extradata->list_unsubscribe)
1319 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1320 if (!msginfo->extradata->list_help)
1321 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1322 if (!msginfo->extradata->list_archive)
1323 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1324 if (!msginfo->extradata->list_owner)
1325 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1326 if (!msginfo->extradata->avatars)
1327 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1328 (GCopyFunc) procmsg_msginfoavatar_copy);
1329 if (!msginfo->extradata->dispositionnotificationto)
1330 msginfo->extradata->dispositionnotificationto =
1331 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1332 if (!msginfo->extradata->returnreceiptto)
1333 msginfo->extradata->returnreceiptto = g_strdup
1334 (full_msginfo->extradata->returnreceiptto);
1335 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1336 msginfo->extradata->partial_recv = g_strdup
1337 (full_msginfo->extradata->partial_recv);
1338 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1339 msginfo->extradata->account_server = g_strdup
1340 (full_msginfo->extradata->account_server);
1341 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1342 msginfo->extradata->account_login = g_strdup
1343 (full_msginfo->extradata->account_login);
1344 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1345 msginfo->extradata->resent_from = g_strdup
1346 (full_msginfo->extradata->resent_from);
1348 procmsg_msginfo_free(&full_msginfo);
1350 return procmsg_msginfo_new_ref(msginfo);
1353 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1355 MsgInfo *full_msginfo;
1356 gchar *file;
1358 if (msginfo == NULL) return NULL;
1360 file = procmsg_get_message_file_path(msginfo);
1361 if (!file || !is_file_exist(file)) {
1362 g_free(file);
1363 file = procmsg_get_message_file(msginfo);
1365 if (!file || !is_file_exist(file)) {
1366 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1367 return NULL;
1370 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1371 g_free(file);
1372 return full_msginfo;
1375 #define FREENULL(n) { g_free(n); n = NULL; }
1376 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1378 MsgInfo *msginfo = *msginfo_ptr;
1380 if (msginfo == NULL) return;
1382 msginfo->refcnt--;
1383 if (msginfo->refcnt > 0)
1384 return;
1386 if (msginfo->to_folder) {
1387 msginfo->to_folder->op_count--;
1388 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1391 FREENULL(msginfo->fromspace);
1393 FREENULL(msginfo->fromname);
1395 FREENULL(msginfo->date);
1396 FREENULL(msginfo->from);
1397 FREENULL(msginfo->to);
1398 FREENULL(msginfo->cc);
1399 FREENULL(msginfo->newsgroups);
1400 FREENULL(msginfo->subject);
1401 FREENULL(msginfo->msgid);
1402 FREENULL(msginfo->inreplyto);
1403 FREENULL(msginfo->xref);
1405 if (msginfo->extradata) {
1406 if (msginfo->extradata->avatars) {
1407 g_slist_foreach(msginfo->extradata->avatars,
1408 (GFunc)procmsg_msginfoavatar_free,
1409 NULL);
1410 g_slist_free(msginfo->extradata->avatars);
1411 msginfo->extradata->avatars = NULL;
1413 FREENULL(msginfo->extradata->returnreceiptto);
1414 FREENULL(msginfo->extradata->dispositionnotificationto);
1415 FREENULL(msginfo->extradata->list_post);
1416 FREENULL(msginfo->extradata->list_subscribe);
1417 FREENULL(msginfo->extradata->list_unsubscribe);
1418 FREENULL(msginfo->extradata->list_help);
1419 FREENULL(msginfo->extradata->list_archive);
1420 FREENULL(msginfo->extradata->list_owner);
1421 FREENULL(msginfo->extradata->partial_recv);
1422 FREENULL(msginfo->extradata->account_server);
1423 FREENULL(msginfo->extradata->account_login);
1424 FREENULL(msginfo->extradata->resent_from);
1425 FREENULL(msginfo->extradata);
1427 slist_free_strings_full(msginfo->references);
1428 msginfo->references = NULL;
1429 g_slist_free(msginfo->tags);
1430 msginfo->tags = NULL;
1432 FREENULL(msginfo->plaintext_file);
1434 g_free(msginfo);
1435 *msginfo_ptr = NULL;
1437 #undef FREENULL
1439 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1441 guint memusage = 0;
1442 GSList *tmp;
1444 memusage += sizeof(MsgInfo);
1445 if (msginfo->fromname)
1446 memusage += strlen(msginfo->fromname);
1447 if (msginfo->date)
1448 memusage += strlen(msginfo->date);
1449 if (msginfo->from)
1450 memusage += strlen(msginfo->from);
1451 if (msginfo->to)
1452 memusage += strlen(msginfo->to);
1453 if (msginfo->cc)
1454 memusage += strlen(msginfo->cc);
1455 if (msginfo->newsgroups)
1456 memusage += strlen(msginfo->newsgroups);
1457 if (msginfo->subject)
1458 memusage += strlen(msginfo->subject);
1459 if (msginfo->msgid)
1460 memusage += strlen(msginfo->msgid);
1461 if (msginfo->inreplyto)
1462 memusage += strlen(msginfo->inreplyto);
1464 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1465 gchar *r = (gchar *)tmp->data;
1466 memusage += r?strlen(r):0 + sizeof(GSList);
1468 if (msginfo->fromspace)
1469 memusage += strlen(msginfo->fromspace);
1471 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1472 memusage += sizeof(GSList);
1474 if (msginfo->extradata) {
1475 memusage += sizeof(MsgInfoExtraData);
1476 if (msginfo->extradata->avatars) {
1477 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1478 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1479 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1480 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1483 if (msginfo->extradata->dispositionnotificationto)
1484 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1485 if (msginfo->extradata->returnreceiptto)
1486 memusage += strlen(msginfo->extradata->returnreceiptto);
1488 if (msginfo->extradata->partial_recv)
1489 memusage += strlen(msginfo->extradata->partial_recv);
1490 if (msginfo->extradata->account_server)
1491 memusage += strlen(msginfo->extradata->account_server);
1492 if (msginfo->extradata->account_login)
1493 memusage += strlen(msginfo->extradata->account_login);
1494 if (msginfo->extradata->resent_from)
1495 memusage += strlen(msginfo->extradata->resent_from);
1497 if (msginfo->extradata->list_post)
1498 memusage += strlen(msginfo->extradata->list_post);
1499 if (msginfo->extradata->list_subscribe)
1500 memusage += strlen(msginfo->extradata->list_subscribe);
1501 if (msginfo->extradata->list_unsubscribe)
1502 memusage += strlen(msginfo->extradata->list_unsubscribe);
1503 if (msginfo->extradata->list_help)
1504 memusage += strlen(msginfo->extradata->list_help);
1505 if (msginfo->extradata->list_archive)
1506 memusage += strlen(msginfo->extradata->list_archive);
1507 if (msginfo->extradata->list_owner)
1508 memusage += strlen(msginfo->extradata->list_owner);
1510 return memusage;
1513 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1514 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1516 static HeaderEntry qentry[] = {
1517 {"S:", NULL, FALSE}, /* 0 */
1518 {"SSV:", NULL, FALSE},
1519 {"R:", NULL, FALSE},
1520 {"NG:", NULL, FALSE},
1521 {"MAID:", NULL, FALSE},
1522 {"NAID:", NULL, FALSE}, /* 5 */
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}, /* 10 */
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}, /* 15 */
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 buf[BUFFSIZE];
1546 gint hnum;
1547 PrefsAccount *mailac = NULL, *newsac = NULL;
1548 gboolean encrypt = FALSE;
1549 FolderItem *outbox;
1551 cm_return_val_if_fail(file != NULL, -1);
1553 if ((fp = g_fopen(file, "rb")) == NULL) {
1554 FILE_OP_ERROR(file, "fopen");
1555 if (errstr) {
1556 if (*errstr) g_free(*errstr);
1557 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1559 return -1;
1562 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1563 != -1) {
1564 gchar *p = buf + strlen(qentry[hnum].name);
1566 switch (hnum) {
1567 case Q_SENDER:
1568 if (from == NULL)
1569 from = g_strdup(p);
1570 break;
1571 case Q_SMTPSERVER:
1572 if (smtpserver == NULL)
1573 smtpserver = g_strdup(p);
1574 break;
1575 case Q_RECIPIENTS:
1576 to_list = address_list_append(to_list, p);
1577 break;
1578 case Q_NEWSGROUPS:
1579 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1580 break;
1581 case Q_MAIL_ACCOUNT_ID:
1582 mailac = account_find_from_id(atoi(p));
1583 break;
1584 case Q_NEWS_ACCOUNT_ID:
1585 newsac = account_find_from_id(atoi(p));
1586 break;
1587 case Q_SAVE_COPY_FOLDER:
1588 if (savecopyfolder == NULL)
1589 savecopyfolder = g_strdup(p);
1590 break;
1591 case Q_REPLY_MESSAGE_ID:
1592 if (replymessageid == NULL)
1593 replymessageid = g_strdup(p);
1594 break;
1595 case Q_FWD_MESSAGE_ID:
1596 if (fwdmessageid == NULL)
1597 fwdmessageid = g_strdup(p);
1598 break;
1599 case Q_ENCRYPT:
1600 case Q_ENCRYPT_OLD:
1601 if (p[0] == '1')
1602 encrypt = TRUE;
1603 break;
1604 case Q_CLAWS_HDRS:
1605 case Q_CLAWS_HDRS_OLD:
1606 /* end of special headers reached */
1607 goto send_mail; /* can't "break;break;" */
1610 send_mail:
1611 filepos = ftell(fp);
1612 if (filepos < 0) {
1613 FILE_OP_ERROR(file, "ftell");
1614 if (errstr) {
1615 if (*errstr) g_free(*errstr);
1616 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1618 return -1;
1621 if (to_list) {
1622 debug_print("Sending message by mail\n");
1623 if (!from) {
1624 if (errstr) {
1625 if (*errstr) g_free(*errstr);
1626 *errstr = g_strdup_printf(_("Queued message header is broken."));
1628 mailval = -1;
1629 } else if (mailac && mailac->use_mail_command &&
1630 mailac->mail_command && (* mailac->mail_command)) {
1631 mailval = send_message_local(mailac->mail_command, fp);
1632 } else {
1633 if (!mailac) {
1634 mailac = account_find_from_smtp_server(from, smtpserver);
1635 if (!mailac) {
1636 g_warning("Account not found. "
1637 "Using current account...");
1638 mailac = cur_account;
1642 if (mailac) {
1643 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1644 if (mailval == -1 && errstr) {
1645 if (*errstr) g_free(*errstr);
1646 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1648 } else {
1649 PrefsAccount tmp_ac;
1651 g_warning("Account not found.");
1653 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1654 tmp_ac.address = from;
1655 tmp_ac.smtp_server = smtpserver;
1656 tmp_ac.smtpport = SMTP_PORT;
1657 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1658 if (mailval == -1 && errstr) {
1659 if (*errstr) g_free(*errstr);
1660 *errstr = g_strdup_printf(_("No specific account has been found to "
1661 "send, and an error happened during SMTP session."));
1665 } else if (!to_list && !newsgroup_list) {
1666 if (errstr) {
1667 if (*errstr) g_free(*errstr);
1668 *errstr = g_strdup(_("Couldn't determine sending information. "
1669 "Maybe the email hasn't been generated by Claws Mail."));
1671 mailval = -1;
1674 if (fseek(fp, filepos, SEEK_SET) < 0) {
1675 FILE_OP_ERROR(file, "fseek");
1676 mailval = -1;
1679 if (newsgroup_list && newsac && (mailval == 0)) {
1680 Folder *folder;
1681 gchar *tmp = NULL;
1682 FILE *tmpfp;
1684 /* write to temporary file */
1685 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1686 G_DIR_SEPARATOR, file);
1687 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1688 FILE_OP_ERROR(tmp, "fopen");
1689 newsval = -1;
1690 alertpanel_error(_("Couldn't create temporary file for news sending."));
1691 } else {
1692 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1693 FILE_OP_ERROR(tmp, "chmod");
1694 g_warning("can't change file mode");
1697 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1698 if (fputs(buf, tmpfp) == EOF) {
1699 FILE_OP_ERROR(tmp, "fputs");
1700 newsval = -1;
1701 if (errstr) {
1702 if (*errstr) g_free(*errstr);
1703 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1707 fclose(tmpfp);
1709 if (newsval == 0) {
1710 debug_print("Sending message by news\n");
1712 folder = FOLDER(newsac->folder);
1714 newsval = news_post(folder, tmp);
1715 if (newsval < 0 && errstr) {
1716 if (*errstr) g_free(*errstr);
1717 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1718 newsac->nntp_server);
1721 claws_unlink(tmp);
1723 g_free(tmp);
1726 fclose(fp);
1728 /* update session statistics */
1729 if (mailval == 0 && newsval == 0) {
1730 /* update session stats */
1731 if (replymessageid)
1732 session_stats.replied++;
1733 else if (fwdmessageid)
1734 session_stats.forwarded++;
1735 else
1736 session_stats.sent++;
1739 /* save message to outbox */
1740 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1741 debug_print("saving sent message...\n");
1743 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1744 outbox = folder_find_item_from_identifier(savecopyfolder);
1745 if (!outbox)
1746 outbox = folder_get_default_outbox();
1748 /* Mail was not saved to outbox before encrypting, save it now. */
1749 gboolean saved = FALSE;
1750 *queued_removed = FALSE;
1751 if (queue && msgnum > 0) {
1752 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1753 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1754 debug_print("moved queued mail %d to sent folder\n", msgnum);
1755 saved = TRUE;
1756 *queued_removed = TRUE;
1757 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1758 debug_print("copied queued mail %d to sent folder\n", msgnum);
1759 saved = TRUE;
1761 procmsg_msginfo_free(&queued_mail);
1763 if (!saved) {
1764 debug_print("resaving queued mail to sent folder\n");
1765 procmsg_save_to_outbox(outbox, file, TRUE);
1770 if (replymessageid != NULL || fwdmessageid != NULL) {
1771 gchar **tokens;
1772 FolderItem *item;
1774 if (replymessageid != NULL)
1775 tokens = g_strsplit(replymessageid, "\t", 0);
1776 else
1777 tokens = g_strsplit(fwdmessageid, "\t", 0);
1778 item = folder_find_item_from_identifier(tokens[0]);
1780 /* check if queued message has valid folder and message id */
1781 if (item != NULL && tokens[2] != NULL) {
1782 MsgInfo *msginfo;
1784 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1786 /* check if referring message exists and has a message id */
1787 if ((msginfo != NULL) &&
1788 (msginfo->msgid != NULL) &&
1789 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1790 procmsg_msginfo_free(&msginfo);
1791 msginfo = NULL;
1794 if (msginfo == NULL) {
1795 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1798 if (msginfo != NULL) {
1799 if (replymessageid != NULL) {
1800 MsgPermFlags to_unset = 0;
1802 if (prefs_common.mark_as_read_on_new_window)
1803 to_unset = (MSG_NEW|MSG_UNREAD);
1805 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1806 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1807 } else {
1808 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1810 procmsg_msginfo_free(&msginfo);
1813 g_strfreev(tokens);
1816 g_free(from);
1817 g_free(smtpserver);
1818 slist_free_strings_full(to_list);
1819 slist_free_strings_full(newsgroup_list);
1820 g_free(savecopyfolder);
1821 g_free(replymessageid);
1822 g_free(fwdmessageid);
1824 return (newsval != 0 ? newsval : mailval);
1827 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1829 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1830 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1831 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1832 return result;
1835 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1837 gint val;
1838 if (procmsg_queue_lock(errstr)) {
1839 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1840 procmsg_queue_unlock();
1841 return val;
1843 return -1;
1846 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1848 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1850 /* NEW flag */
1851 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1852 item->new_msgs++;
1855 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1856 item->new_msgs--;
1859 /* UNREAD flag */
1860 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1861 item->unread_msgs++;
1862 if (procmsg_msg_has_marked_parent(msginfo))
1863 item->unreadmarked_msgs++;
1866 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1867 item->unread_msgs--;
1868 if (procmsg_msg_has_marked_parent(msginfo))
1869 item->unreadmarked_msgs--;
1872 /* MARK flag */
1873 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1874 procmsg_update_unread_children(msginfo, TRUE);
1875 item->marked_msgs++;
1878 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1879 procmsg_update_unread_children(msginfo, FALSE);
1880 item->marked_msgs--;
1883 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1884 item->replied_msgs++;
1887 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1888 item->replied_msgs--;
1891 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1892 item->forwarded_msgs++;
1895 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1896 item->forwarded_msgs--;
1899 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1900 item->locked_msgs++;
1903 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1904 item->locked_msgs--;
1907 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1908 item->ignored_msgs--;
1911 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1912 item->ignored_msgs++;
1915 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1916 item->watched_msgs--;
1919 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1920 item->watched_msgs++;
1924 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1926 FolderItem *item;
1927 MsgInfoUpdate msginfo_update;
1928 MsgPermFlags perm_flags_new, perm_flags_old;
1929 MsgTmpFlags tmp_flags_old;
1931 cm_return_if_fail(msginfo != NULL);
1932 item = msginfo->folder;
1933 cm_return_if_fail(item != NULL);
1935 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1937 /* Perm Flags handling */
1938 perm_flags_old = msginfo->flags.perm_flags;
1939 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1940 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1941 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1943 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1944 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1947 if (perm_flags_old != perm_flags_new) {
1948 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1950 update_folder_msg_counts(item, msginfo, perm_flags_old);
1951 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1954 /* Tmp flags handling */
1955 tmp_flags_old = msginfo->flags.tmp_flags;
1956 msginfo->flags.tmp_flags |= tmp_flags;
1958 /* update notification */
1959 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1960 msginfo_update.msginfo = msginfo;
1961 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1962 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1963 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1967 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1969 FolderItem *item;
1970 MsgInfoUpdate msginfo_update;
1971 MsgPermFlags perm_flags_new, perm_flags_old;
1972 MsgTmpFlags tmp_flags_old;
1974 cm_return_if_fail(msginfo != NULL);
1975 item = msginfo->folder;
1976 cm_return_if_fail(item != NULL);
1978 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1980 /* Perm Flags handling */
1981 perm_flags_old = msginfo->flags.perm_flags;
1982 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1984 if (perm_flags_old != perm_flags_new) {
1985 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1987 update_folder_msg_counts(item, msginfo, perm_flags_old);
1990 /* Tmp flags hanlding */
1991 tmp_flags_old = msginfo->flags.tmp_flags;
1992 msginfo->flags.tmp_flags &= ~tmp_flags;
1994 /* update notification */
1995 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1996 msginfo_update.msginfo = msginfo;
1997 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1998 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1999 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2003 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2004 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2005 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2007 FolderItem *item;
2008 MsgInfoUpdate msginfo_update;
2009 MsgPermFlags perm_flags_new, perm_flags_old;
2010 MsgTmpFlags tmp_flags_old;
2012 cm_return_if_fail(msginfo != NULL);
2013 item = msginfo->folder;
2014 cm_return_if_fail(item != NULL);
2016 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2018 /* Perm Flags handling */
2019 perm_flags_old = msginfo->flags.perm_flags;
2020 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2021 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2022 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2024 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2025 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2028 if (perm_flags_old != perm_flags_new) {
2029 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2031 update_folder_msg_counts(item, msginfo, perm_flags_old);
2035 /* Tmp flags handling */
2036 tmp_flags_old = msginfo->flags.tmp_flags;
2037 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2038 msginfo->flags.tmp_flags |= add_tmp_flags;
2040 /* update notification */
2041 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2042 msginfo_update.msginfo = msginfo;
2043 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2044 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2045 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2050 *\brief check for flags (e.g. mark) in prior msgs of current thread
2052 *\param info Current message
2053 *\param perm_flags Flags to be checked
2054 *\param parentmsgs Hash of prior msgs to avoid loops
2056 *\return gboolean TRUE if perm_flags are found
2058 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2059 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2061 MsgInfo *tmp;
2063 cm_return_val_if_fail(info != NULL, FALSE);
2065 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2066 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2067 info->inreplyto);
2068 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2069 procmsg_msginfo_free(&tmp);
2070 return TRUE;
2071 } else if (tmp != NULL) {
2072 gboolean result;
2074 if (g_hash_table_lookup(parentmsgs, info)) {
2075 debug_print("loop detected: %d\n",
2076 info->msgnum);
2077 result = FALSE;
2078 } else {
2079 g_hash_table_insert(parentmsgs, info, "1");
2080 result = procmsg_msg_has_flagged_parent_real(
2081 tmp, perm_flags, parentmsgs);
2083 procmsg_msginfo_free(&tmp);
2084 return result;
2085 } else {
2086 return FALSE;
2088 } else
2089 return FALSE;
2093 *\brief Callback for cleaning up hash of parentmsgs
2095 static gboolean parentmsgs_hash_remove(gpointer key,
2096 gpointer value,
2097 gpointer user_data)
2099 return TRUE;
2103 *\brief Set up list of parentmsgs
2104 * See procmsg_msg_has_flagged_parent_real()
2106 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2108 gboolean result;
2109 static GHashTable *parentmsgs = NULL;
2111 if (parentmsgs == NULL)
2112 parentmsgs = g_hash_table_new(NULL, NULL);
2114 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2115 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2117 return result;
2121 *\brief Check if msgs prior in thread are marked
2122 * See procmsg_msg_has_flagged_parent_real()
2124 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2126 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2130 static GSList *procmsg_find_children_func(MsgInfo *info,
2131 GSList *children, GSList *all)
2133 GSList *cur;
2135 cm_return_val_if_fail(info!=NULL, children);
2136 if (info->msgid == NULL)
2137 return children;
2139 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2140 MsgInfo *tmp = (MsgInfo *)cur->data;
2141 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2142 /* Check if message is already in the list */
2143 if ((children == NULL) ||
2144 (g_slist_index(children, tmp) == -1)) {
2145 children = g_slist_prepend(children,
2146 procmsg_msginfo_new_ref(tmp));
2147 children = procmsg_find_children_func(tmp,
2148 children,
2149 all);
2153 return children;
2156 static GSList *procmsg_find_children (MsgInfo *info)
2158 GSList *children;
2159 GSList *all, *cur;
2161 cm_return_val_if_fail(info!=NULL, NULL);
2162 all = folder_item_get_msg_list(info->folder);
2163 children = procmsg_find_children_func(info, NULL, all);
2164 if (children != NULL) {
2165 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2166 /* this will not free the used pointers
2167 created with procmsg_msginfo_new_ref */
2168 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2171 g_slist_free(all);
2173 return children;
2176 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2178 GSList *children = procmsg_find_children(info);
2179 GSList *cur;
2180 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2181 MsgInfo *tmp = (MsgInfo *)cur->data;
2182 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2183 if(newly_marked)
2184 info->folder->unreadmarked_msgs++;
2185 else
2186 info->folder->unreadmarked_msgs--;
2187 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2189 procmsg_msginfo_free(&tmp);
2191 g_slist_free(children);
2195 * Set the destination folder for a copy or move operation
2197 * \param msginfo The message which's destination folder is changed
2198 * \param to_folder The destination folder for the operation
2200 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2202 if(msginfo->to_folder != NULL) {
2203 msginfo->to_folder->op_count--;
2204 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2206 msginfo->to_folder = to_folder;
2207 if(to_folder != NULL) {
2208 to_folder->op_count++;
2209 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2214 * Apply filtering actions to the msginfo
2216 * \param msginfo The MsgInfo describing the message that should be filtered
2217 * \return TRUE if the message was moved and MsgInfo is now invalid,
2218 * FALSE otherwise
2220 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2222 MailFilteringData mail_filtering_data;
2224 mail_filtering_data.msginfo = msginfo;
2225 mail_filtering_data.msglist = NULL;
2226 mail_filtering_data.filtered = NULL;
2227 mail_filtering_data.unfiltered = NULL;
2228 mail_filtering_data.account = ac_prefs;
2230 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2231 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2232 return TRUE;
2234 /* filter if enabled in prefs or move to inbox if not */
2235 if((filtering_rules != NULL) &&
2236 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2237 FILTERING_INCORPORATION, NULL)) {
2238 return TRUE;
2241 return FALSE;
2244 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2245 GSList **filtered, GSList **unfiltered,
2246 gboolean do_filter)
2248 GSList *cur, *to_do = NULL;
2249 gint total = 0, curnum = 0;
2250 MailFilteringData mail_filtering_data;
2252 cm_return_if_fail(filtered != NULL);
2253 cm_return_if_fail(unfiltered != NULL);
2255 *filtered = NULL;
2256 *unfiltered = NULL;
2258 if (list == NULL)
2259 return;
2261 total = g_slist_length(list);
2263 if (!do_filter) {
2264 *filtered = NULL;
2265 *unfiltered = g_slist_copy(list);
2266 return;
2269 statusbar_print_all(_("Filtering messages...\n"));
2271 mail_filtering_data.msginfo = NULL;
2272 mail_filtering_data.msglist = list;
2273 mail_filtering_data.filtered = NULL;
2274 mail_filtering_data.unfiltered = NULL;
2275 mail_filtering_data.account = ac;
2277 if (!ac || ac->filterhook_on_recv)
2278 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2280 if (mail_filtering_data.filtered == NULL &&
2281 mail_filtering_data.unfiltered == NULL) {
2282 /* nothing happened */
2283 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2284 to_do = list;
2286 if (mail_filtering_data.filtered != NULL) {
2287 /* keep track of what's been filtered by the hooks */
2288 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2289 g_slist_length(list),
2290 g_slist_length(mail_filtering_data.filtered),
2291 g_slist_length(mail_filtering_data.unfiltered));
2293 *filtered = g_slist_copy(mail_filtering_data.filtered);
2295 if (mail_filtering_data.unfiltered != NULL) {
2296 /* what the hooks didn't handle will go in filtered or
2297 * unfiltered in the next loop */
2298 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2299 g_slist_length(list),
2300 g_slist_length(mail_filtering_data.filtered),
2301 g_slist_length(mail_filtering_data.unfiltered));
2302 to_do = mail_filtering_data.unfiltered;
2305 for (cur = to_do; cur; cur = cur->next) {
2306 MsgInfo *info = (MsgInfo *)cur->data;
2307 if (procmsg_msginfo_filter(info, ac))
2308 *filtered = g_slist_prepend(*filtered, info);
2309 else
2310 *unfiltered = g_slist_prepend(*unfiltered, info);
2311 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2314 g_slist_free(mail_filtering_data.filtered);
2315 g_slist_free(mail_filtering_data.unfiltered);
2317 *filtered = g_slist_reverse(*filtered);
2318 *unfiltered = g_slist_reverse(*unfiltered);
2320 statusbar_progress_all(0,0,0);
2321 statusbar_pop_all();
2324 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2326 MsgInfo *tmp_msginfo = NULL;
2327 MsgFlags flags = {0, 0};
2328 gchar *tmpfile = get_tmp_file();
2329 FILE *fp = g_fopen(tmpfile, "wb");
2331 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2332 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2333 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2334 if (fp)
2335 fclose(fp);
2336 g_free(tmpfile);
2337 return NULL;
2340 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2341 fclose(fp);
2342 fp = NULL;
2343 tmp_msginfo = procheader_parse_file(
2344 tmpfile, flags,
2345 TRUE, FALSE);
2347 if (fp)
2348 fclose(fp);
2350 if (tmp_msginfo != NULL) {
2351 if (src_msginfo)
2352 tmp_msginfo->folder = src_msginfo->folder;
2353 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2354 } else {
2355 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2358 g_free(tmpfile);
2360 return tmp_msginfo;
2363 static GSList *spam_learners = NULL;
2365 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2367 if (!g_slist_find(spam_learners, learn_func))
2368 spam_learners = g_slist_append(spam_learners, learn_func);
2369 if (mainwindow_get_mainwindow()) {
2370 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2371 summary_set_menu_sensitive(
2372 mainwindow_get_mainwindow()->summaryview);
2373 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2377 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2379 spam_learners = g_slist_remove(spam_learners, learn_func);
2380 if (mainwindow_get_mainwindow()) {
2381 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2382 summary_set_menu_sensitive(
2383 mainwindow_get_mainwindow()->summaryview);
2384 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2388 gboolean procmsg_spam_can_learn(void)
2390 return g_slist_length(spam_learners) > 0;
2393 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2395 GSList *cur = spam_learners;
2396 int ret = 0;
2397 for (; cur; cur = cur->next) {
2398 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2399 ret |= func(info, list, spam);
2401 return ret;
2404 static gchar *spam_folder_item = NULL;
2405 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2406 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2408 g_free(spam_folder_item);
2409 if (item_identifier)
2410 spam_folder_item = g_strdup(item_identifier);
2411 else
2412 spam_folder_item = NULL;
2413 if (spam_get_folder_func != NULL)
2414 procmsg_spam_get_folder_func = spam_get_folder_func;
2415 else
2416 procmsg_spam_get_folder_func = NULL;
2419 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2421 FolderItem *item = NULL;
2423 if (procmsg_spam_get_folder_func)
2424 item = procmsg_spam_get_folder_func(msginfo);
2425 if (item == NULL && spam_folder_item)
2426 item = folder_find_item_from_identifier(spam_folder_item);
2427 if (item == NULL)
2428 item = folder_get_default_trash();
2429 return item;
2432 static void item_has_queued_mails(FolderItem *item, gpointer data)
2434 gboolean *result = (gboolean *)data;
2435 if (*result == TRUE)
2436 return;
2437 if (folder_has_parent_of_type(item, F_QUEUE)) {
2438 if (item->total_msgs == 0)
2439 return;
2440 else {
2441 GSList *msglist = folder_item_get_msg_list(item);
2442 GSList *cur;
2443 for (cur = msglist; cur; cur = cur->next) {
2444 MsgInfo *msginfo = (MsgInfo *)cur->data;
2445 if (!MSG_IS_DELETED(msginfo->flags) &&
2446 !MSG_IS_LOCKED(msginfo->flags)) {
2447 *result = TRUE;
2448 break;
2451 procmsg_msg_list_free(msglist);
2456 gboolean procmsg_have_queued_mails_fast (void)
2458 gboolean result = FALSE;
2459 folder_func_to_all_folders(item_has_queued_mails, &result);
2460 return result;
2463 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2465 gboolean *result = (gboolean *)data;
2466 if (*result == TRUE)
2467 return;
2468 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2469 *result = TRUE;
2472 gboolean procmsg_have_trashed_mails_fast (void)
2474 gboolean result = FALSE;
2475 folder_func_to_all_folders(item_has_trashed_mails, &result);
2476 return result;
2479 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2481 GSList *cur = NULL;
2482 gchar *tags = NULL;
2484 if (!msginfo)
2485 return NULL;
2487 if (msginfo->tags == NULL)
2488 return NULL;
2489 for (cur = msginfo->tags; cur; cur = cur->next) {
2490 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2491 if (!tag)
2492 continue;
2493 if (!tags)
2494 tags = g_strdup(tag);
2495 else {
2496 int olen = strlen(tags);
2497 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2498 tags = g_realloc(tags, nlen+1);
2499 if (!tags)
2500 return NULL;
2501 strcpy(tags+olen, ", ");
2502 strcpy(tags+olen+2, tag);
2503 tags[nlen]='\0';
2506 return tags;
2509 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2511 GSList changed;
2513 if (id == 0)
2514 return;
2516 if (!set) {
2517 msginfo->tags = g_slist_remove(
2518 msginfo->tags,
2519 GINT_TO_POINTER(id));
2520 changed.data = GINT_TO_POINTER(id);
2521 changed.next = NULL;
2522 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2523 } else {
2524 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2525 msginfo->tags = g_slist_append(
2526 msginfo->tags,
2527 GINT_TO_POINTER(id));
2529 changed.data = GINT_TO_POINTER(id);
2530 changed.next = NULL;
2531 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2536 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2538 GSList *unset = msginfo->tags;
2539 msginfo->tags = NULL;
2540 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2541 g_slist_free(unset);