Add missing library to output of --version-full
[claws.git] / src / procmsg.c
blobc9b077eb1a90d02ffc4b532a76d929a01bedbcd5
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 = NULL;
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, fp, qentry)) != -1 && buf != NULL) {
754 gchar *p = buf + strlen(qentry[hnum].name);
756 if (hnum == Q_MAIL_ACCOUNT_ID) {
757 mailac = account_find_from_id(atoi(p));
758 break;
760 g_free(buf);
761 buf = NULL;
763 fclose(fp);
764 return mailac;
767 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
769 GSList *mia;
771 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
772 return NULL;
774 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
775 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
776 if (avatar->avatar_id == type)
777 return avatar->avatar_src;
780 return NULL;
783 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
785 MsgInfoAvatar *av;
787 if (!msginfo->extradata)
788 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
790 av = g_new0(MsgInfoAvatar, 1);
791 av->avatar_id = type;
792 av->avatar_src = g_strdup(data);
794 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
797 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
799 gchar *folder_id;
800 const gchar *msgid;
801 gchar *id;
803 cm_return_val_if_fail(msginfo != NULL, NULL);
804 folder_id = folder_item_get_identifier(msginfo->folder);
805 msgid = msginfo->msgid;
807 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
809 g_free(folder_id);
811 return id;
814 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
816 gchar *folder_id = g_strdup(id);
817 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
818 const gchar *msgid;
819 FolderItem *item;
820 MsgInfo *msginfo;
822 if (separator == NULL) {
823 g_free(folder_id);
824 return NULL;
827 *separator = '\0';
828 msgid = separator + 1;
830 item = folder_find_item_from_identifier(folder_id);
832 if (item == NULL) {
833 g_free(folder_id);
834 return NULL;
837 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
838 g_free(folder_id);
840 return msginfo;
843 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
845 GSList *result = NULL;
846 GSList *orig = NULL;
847 PrefsAccount *last_account = NULL;
848 MsgInfo *msg = NULL;
849 GSList *cur = NULL;
850 gboolean nothing_to_sort = TRUE;
852 if (!list)
853 return NULL;
855 orig = g_slist_copy(list);
857 msg = (MsgInfo *)orig->data;
859 for (cur = orig; cur; cur = cur->next)
860 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
862 debug_print("\n");
864 parse_again:
865 nothing_to_sort = TRUE;
866 cur = orig;
867 while (cur) {
868 gchar *file = NULL;
869 PrefsAccount *ac = NULL;
870 msg = (MsgInfo *)cur->data;
871 file = folder_item_fetch_msg(queue, msg->msgnum);
872 ac = procmsg_get_account_from_file(file);
873 g_free(file);
875 if (last_account == NULL || (ac != NULL && ac == last_account)) {
876 result = g_slist_append(result, msg);
877 orig = g_slist_remove(orig, msg);
878 last_account = ac;
879 nothing_to_sort = FALSE;
880 goto parse_again;
882 cur = cur->next;
885 if (orig && g_slist_length(orig)) {
886 if (!last_account && nothing_to_sort) {
887 /* can't find an account for the rest of the list */
888 cur = orig;
889 while (cur) {
890 result = g_slist_append(result, cur->data);
891 cur = cur->next;
893 } else {
894 last_account = NULL;
895 goto parse_again;
899 g_slist_free(orig);
901 for (cur = result; cur; cur = cur->next)
902 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
904 debug_print("\n");
906 return result;
909 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
911 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
912 PrefsAccount *ac = procmsg_get_account_from_file(file);
913 GSList *cur;
914 g_free(file);
915 for (cur = elem; cur; cur = cur->next) {
916 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
917 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
919 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
920 if (procmsg_get_account_from_file(file) == ac) {
921 g_free(file);
922 return FALSE;
926 g_free(file);
928 return TRUE;
931 static gboolean send_queue_lock = FALSE;
933 gboolean procmsg_queue_lock(char **errstr)
935 if (send_queue_lock) {
936 /* Avoid having to translate two similar strings */
937 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
938 if (errstr) {
939 if (*errstr) g_free(*errstr);
940 *errstr = g_strdup_printf(_("Already trying to send."));
942 return FALSE;
944 send_queue_lock = TRUE;
945 return TRUE;
947 void procmsg_queue_unlock(void)
949 send_queue_lock = FALSE;
952 *\brief Send messages in queue
954 *\param queue Queue folder to process
955 *\param save_msgs Unused
957 *\return Number of messages sent, negative if an error occurred
958 * positive if no error occurred
960 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
962 gint sent = 0, err = 0;
963 GSList *list, *elem;
964 GSList *sorted_list = NULL;
965 GNode *node, *next;
967 if (!procmsg_queue_lock(errstr)) {
968 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
969 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
970 return -1;
972 inc_lock();
973 if (!queue)
974 queue = folder_get_default_queue();
976 if (queue == NULL) {
977 procmsg_queue_unlock();
978 inc_unlock();
979 return -1;
982 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
983 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
985 folder_item_scan(queue);
986 list = folder_item_get_msg_list(queue);
988 /* sort the list per sender account; this helps reusing the same SMTP server */
989 sorted_list = procmsg_list_sort_by_account(queue, list);
991 for (elem = sorted_list; elem != NULL; elem = elem->next) {
992 gchar *file;
993 MsgInfo *msginfo;
995 msginfo = (MsgInfo *)(elem->data);
996 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
997 file = folder_item_fetch_msg(queue, msginfo->msgnum);
998 if (file) {
999 gboolean queued_removed = FALSE;
1000 if (procmsg_send_message_queue_full(file,
1001 !procmsg_is_last_for_account(queue, msginfo, elem),
1002 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1003 g_warning("Sending queued message %d failed.",
1004 msginfo->msgnum);
1005 err++;
1006 } else {
1007 sent++;
1008 if (!queued_removed)
1009 folder_item_remove_msg(queue, msginfo->msgnum);
1011 g_free(file);
1014 /* FIXME: supposedly if only one message is locked, and queue
1015 * is being flushed, the following free says something like
1016 * "freeing msg ## in folder (nil)". */
1017 procmsg_msginfo_free(&msginfo);
1020 g_slist_free(sorted_list);
1021 folder_item_scan(queue);
1023 if (queue->node && queue->node->children) {
1024 node = queue->node->children;
1025 while (node != NULL) {
1026 int res = 0;
1027 next = node->next;
1028 send_queue_lock = FALSE;
1029 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1030 send_queue_lock = TRUE;
1031 if (res < 0)
1032 err = -res;
1033 else
1034 sent += res;
1035 node = next;
1038 procmsg_queue_unlock();
1039 inc_unlock();
1040 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1041 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1043 return (err != 0 ? -err : sent);
1046 gboolean procmsg_is_sending(void)
1048 return send_queue_lock;
1052 *\brief Determine if a queue folder is empty
1054 *\param queue Queue folder to process
1056 *\return TRUE if the queue folder is empty, otherwise return FALSE
1058 gboolean procmsg_queue_is_empty(FolderItem *queue)
1060 GSList *list;
1061 gboolean res = FALSE;
1062 if (!queue)
1063 queue = folder_get_default_queue();
1064 cm_return_val_if_fail(queue != NULL, TRUE);
1066 folder_item_scan(queue);
1067 list = folder_item_get_msg_list(queue);
1068 res = (list == NULL);
1069 procmsg_msg_list_free(list);
1071 if (res == TRUE) {
1072 GNode *node, *next;
1073 if (queue->node && queue->node->children) {
1074 node = queue->node->children;
1075 while (node != NULL) {
1076 next = node->next;
1077 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1078 return FALSE;
1079 node = next;
1083 return res;
1086 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1088 FILE *fp, *outfp;
1089 gchar buf[BUFFSIZE];
1091 if ((fp = g_fopen(in, "rb")) == NULL) {
1092 FILE_OP_ERROR(in, "fopen");
1093 return -1;
1095 if ((outfp = g_fopen(out, "wb")) == NULL) {
1096 FILE_OP_ERROR(out, "fopen");
1097 fclose(fp);
1098 return -1;
1100 while (fgets(buf, sizeof(buf), fp) != NULL) {
1101 /* new way */
1102 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1103 strlen("X-Claws-End-Special-Headers:"))) ||
1104 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1105 strlen("X-Sylpheed-End-Special-Headers:"))))
1106 break;
1107 /* old way */
1108 if (buf[0] == '\r' || buf[0] == '\n') break;
1109 /* from other mailers */
1110 if (!strncmp(buf, "Date: ", 6)
1111 || !strncmp(buf, "To: ", 4)
1112 || !strncmp(buf, "From: ", 6)
1113 || !strncmp(buf, "Subject: ", 9)) {
1114 rewind(fp);
1115 break;
1118 while (fgets(buf, sizeof(buf), fp) != NULL) {
1119 if (fputs(buf, outfp) == EOF) {
1120 FILE_OP_ERROR(out, "fputs");
1121 fclose(outfp);
1122 fclose(fp);
1123 return -1;
1126 fclose(outfp);
1127 fclose(fp);
1128 return 0;
1131 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1132 gboolean is_queued)
1134 gint num;
1135 MsgInfo *msginfo, *tmp_msginfo;
1136 MsgFlags flag = {0, 0};
1138 debug_print("saving sent message...\n");
1140 if (!outbox)
1141 outbox = folder_get_default_outbox();
1142 cm_return_val_if_fail(outbox != NULL, -1);
1144 /* remove queueing headers */
1145 if (is_queued) {
1146 gchar tmp[MAXPATHLEN + 1];
1148 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1149 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1151 if (procmsg_remove_special_headers(file, tmp) !=0)
1152 return -1;
1154 folder_item_scan(outbox);
1155 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1156 g_warning("can't save message");
1157 claws_unlink(tmp);
1158 return -1;
1160 } else {
1161 folder_item_scan(outbox);
1162 if ((num = folder_item_add_msg
1163 (outbox, file, &flag, FALSE)) < 0) {
1164 g_warning("can't save message");
1165 return -1;
1168 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1169 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1170 if (msginfo != NULL) {
1171 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1172 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1173 /* tmp_msginfo == msginfo */
1174 if (tmp_msginfo && msginfo->extradata &&
1175 (msginfo->extradata->dispositionnotificationto ||
1176 msginfo->extradata->returnreceiptto)) {
1177 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1179 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1182 return 0;
1186 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1188 msginfo->refcnt++;
1190 return msginfo;
1193 MsgInfo *procmsg_msginfo_new(void)
1195 MsgInfo *newmsginfo;
1197 newmsginfo = g_new0(MsgInfo, 1);
1198 newmsginfo->refcnt = 1;
1200 return newmsginfo;
1203 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1205 MsgInfoAvatar *newavatar;
1207 if (avatar == NULL) return NULL;
1209 newavatar = g_new0(MsgInfoAvatar, 1);
1210 newavatar->avatar_id = avatar->avatar_id;
1211 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1213 return newavatar;
1216 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1218 if (avatar != NULL) {
1219 if (avatar->avatar_src != NULL)
1220 g_free(avatar->avatar_src);
1221 g_free(avatar);
1225 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1227 MsgInfo *newmsginfo;
1228 GSList *refs;
1230 if (msginfo == NULL) return NULL;
1232 newmsginfo = g_new0(MsgInfo, 1);
1234 newmsginfo->refcnt = 1;
1236 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1237 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1238 g_strdup(msginfo->mmb) : NULL
1240 MEMBCOPY(msgnum);
1241 MEMBCOPY(size);
1242 MEMBCOPY(mtime);
1243 MEMBCOPY(date_t);
1245 MEMBCOPY(flags);
1247 MEMBDUP(fromname);
1249 MEMBDUP(date);
1250 MEMBDUP(from);
1251 MEMBDUP(to);
1252 MEMBDUP(cc);
1253 MEMBDUP(newsgroups);
1254 MEMBDUP(subject);
1255 MEMBDUP(msgid);
1256 MEMBDUP(inreplyto);
1257 MEMBDUP(xref);
1259 MEMBCOPY(folder);
1260 MEMBCOPY(to_folder);
1262 if (msginfo->extradata) {
1263 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1264 if (msginfo->extradata->avatars) {
1265 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1266 (GCopyFunc) procmsg_msginfoavatar_copy);
1268 MEMBDUP(extradata->dispositionnotificationto);
1269 MEMBDUP(extradata->returnreceiptto);
1270 MEMBDUP(extradata->partial_recv);
1271 MEMBDUP(extradata->account_server);
1272 MEMBDUP(extradata->account_login);
1273 MEMBDUP(extradata->list_post);
1274 MEMBDUP(extradata->list_subscribe);
1275 MEMBDUP(extradata->list_unsubscribe);
1276 MEMBDUP(extradata->list_help);
1277 MEMBDUP(extradata->list_archive);
1278 MEMBDUP(extradata->list_owner);
1279 MEMBDUP(extradata->resent_from);
1282 refs = msginfo->references;
1283 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1284 newmsginfo->references = g_slist_prepend
1285 (newmsginfo->references, g_strdup(refs->data));
1287 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1289 MEMBCOPY(score);
1290 MEMBDUP(plaintext_file);
1292 return newmsginfo;
1295 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1297 MsgInfo *full_msginfo;
1299 if (msginfo == NULL) return NULL;
1301 if (!file || !is_file_exist(file)) {
1302 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1303 return NULL;
1306 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1307 if (!full_msginfo) return NULL;
1309 msginfo->total_size = full_msginfo->total_size;
1310 msginfo->planned_download = full_msginfo->planned_download;
1312 if (full_msginfo->extradata) {
1313 if (!msginfo->extradata)
1314 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1315 if (!msginfo->extradata->list_post)
1316 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1317 if (!msginfo->extradata->list_subscribe)
1318 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1319 if (!msginfo->extradata->list_unsubscribe)
1320 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1321 if (!msginfo->extradata->list_help)
1322 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1323 if (!msginfo->extradata->list_archive)
1324 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1325 if (!msginfo->extradata->list_owner)
1326 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1327 if (!msginfo->extradata->avatars)
1328 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1329 (GCopyFunc) procmsg_msginfoavatar_copy);
1330 if (!msginfo->extradata->dispositionnotificationto)
1331 msginfo->extradata->dispositionnotificationto =
1332 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1333 if (!msginfo->extradata->returnreceiptto)
1334 msginfo->extradata->returnreceiptto = g_strdup
1335 (full_msginfo->extradata->returnreceiptto);
1336 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1337 msginfo->extradata->partial_recv = g_strdup
1338 (full_msginfo->extradata->partial_recv);
1339 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1340 msginfo->extradata->account_server = g_strdup
1341 (full_msginfo->extradata->account_server);
1342 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1343 msginfo->extradata->account_login = g_strdup
1344 (full_msginfo->extradata->account_login);
1345 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1346 msginfo->extradata->resent_from = g_strdup
1347 (full_msginfo->extradata->resent_from);
1349 procmsg_msginfo_free(&full_msginfo);
1351 return procmsg_msginfo_new_ref(msginfo);
1354 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1356 MsgInfo *full_msginfo;
1357 gchar *file;
1359 if (msginfo == NULL) return NULL;
1361 file = procmsg_get_message_file_path(msginfo);
1362 if (!file || !is_file_exist(file)) {
1363 g_free(file);
1364 file = procmsg_get_message_file(msginfo);
1366 if (!file || !is_file_exist(file)) {
1367 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1368 return NULL;
1371 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1372 g_free(file);
1373 return full_msginfo;
1376 #define FREENULL(n) { g_free(n); n = NULL; }
1377 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1379 MsgInfo *msginfo = *msginfo_ptr;
1381 if (msginfo == NULL) return;
1383 msginfo->refcnt--;
1384 if (msginfo->refcnt > 0)
1385 return;
1387 if (msginfo->to_folder) {
1388 msginfo->to_folder->op_count--;
1389 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1392 FREENULL(msginfo->fromspace);
1394 FREENULL(msginfo->fromname);
1396 FREENULL(msginfo->date);
1397 FREENULL(msginfo->from);
1398 FREENULL(msginfo->to);
1399 FREENULL(msginfo->cc);
1400 FREENULL(msginfo->newsgroups);
1401 FREENULL(msginfo->subject);
1402 FREENULL(msginfo->msgid);
1403 FREENULL(msginfo->inreplyto);
1404 FREENULL(msginfo->xref);
1406 if (msginfo->extradata) {
1407 if (msginfo->extradata->avatars) {
1408 g_slist_foreach(msginfo->extradata->avatars,
1409 (GFunc)procmsg_msginfoavatar_free,
1410 NULL);
1411 g_slist_free(msginfo->extradata->avatars);
1412 msginfo->extradata->avatars = NULL;
1414 FREENULL(msginfo->extradata->returnreceiptto);
1415 FREENULL(msginfo->extradata->dispositionnotificationto);
1416 FREENULL(msginfo->extradata->list_post);
1417 FREENULL(msginfo->extradata->list_subscribe);
1418 FREENULL(msginfo->extradata->list_unsubscribe);
1419 FREENULL(msginfo->extradata->list_help);
1420 FREENULL(msginfo->extradata->list_archive);
1421 FREENULL(msginfo->extradata->list_owner);
1422 FREENULL(msginfo->extradata->partial_recv);
1423 FREENULL(msginfo->extradata->account_server);
1424 FREENULL(msginfo->extradata->account_login);
1425 FREENULL(msginfo->extradata->resent_from);
1426 FREENULL(msginfo->extradata);
1428 slist_free_strings_full(msginfo->references);
1429 msginfo->references = NULL;
1430 g_slist_free(msginfo->tags);
1431 msginfo->tags = NULL;
1433 FREENULL(msginfo->plaintext_file);
1435 g_free(msginfo);
1436 *msginfo_ptr = NULL;
1438 #undef FREENULL
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[] = {
1518 {"S:", NULL, FALSE}, /* 0 */
1519 {"SSV:", NULL, FALSE},
1520 {"R:", NULL, FALSE},
1521 {"NG:", NULL, FALSE},
1522 {"MAID:", NULL, FALSE},
1523 {"NAID:", NULL, FALSE}, /* 5 */
1524 {"SCF:", NULL, FALSE},
1525 {"RMID:", NULL, FALSE},
1526 {"FMID:", NULL, FALSE},
1527 {"X-Claws-Privacy-System:", NULL, FALSE},
1528 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1529 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1530 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1531 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1532 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1533 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1534 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1535 {NULL, NULL, FALSE}};
1536 FILE *fp;
1537 gint filepos;
1538 gint mailval = 0, newsval = 0;
1539 gchar *from = NULL;
1540 gchar *smtpserver = NULL;
1541 GSList *to_list = NULL;
1542 GSList *newsgroup_list = NULL;
1543 gchar *savecopyfolder = NULL;
1544 gchar *replymessageid = NULL;
1545 gchar *fwdmessageid = NULL;
1546 gchar *buf;
1547 gint hnum;
1548 PrefsAccount *mailac = NULL, *newsac = NULL;
1549 gboolean encrypt = FALSE;
1550 FolderItem *outbox;
1552 cm_return_val_if_fail(file != NULL, -1);
1554 if ((fp = g_fopen(file, "rb")) == NULL) {
1555 FILE_OP_ERROR(file, "fopen");
1556 if (errstr) {
1557 if (*errstr) g_free(*errstr);
1558 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1560 return -1;
1563 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
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 g_free(buf);
1612 send_mail:
1613 filepos = ftell(fp);
1614 if (filepos < 0) {
1615 FILE_OP_ERROR(file, "ftell");
1616 if (errstr) {
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1620 return -1;
1623 if (to_list) {
1624 debug_print("Sending message by mail\n");
1625 if (!from) {
1626 if (errstr) {
1627 if (*errstr) g_free(*errstr);
1628 *errstr = g_strdup_printf(_("Queued message header is broken."));
1630 mailval = -1;
1631 } else if (mailac && mailac->use_mail_command &&
1632 mailac->mail_command && (* mailac->mail_command)) {
1633 mailval = send_message_local(mailac->mail_command, fp);
1634 } else {
1635 if (!mailac) {
1636 mailac = account_find_from_smtp_server(from, smtpserver);
1637 if (!mailac) {
1638 g_warning("Account not found. "
1639 "Using current account...");
1640 mailac = cur_account;
1644 if (mailac) {
1645 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1646 if (mailval == -1 && errstr) {
1647 if (*errstr) g_free(*errstr);
1648 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1650 } else {
1651 PrefsAccount tmp_ac;
1653 g_warning("Account not found.");
1655 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1656 tmp_ac.address = from;
1657 tmp_ac.smtp_server = smtpserver;
1658 tmp_ac.smtpport = SMTP_PORT;
1659 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1660 if (mailval == -1 && errstr) {
1661 if (*errstr) g_free(*errstr);
1662 *errstr = g_strdup_printf(_("No specific account has been found to "
1663 "send, and an error happened during SMTP session."));
1667 } else if (!to_list && !newsgroup_list) {
1668 if (errstr) {
1669 if (*errstr) g_free(*errstr);
1670 *errstr = g_strdup(_("Couldn't determine sending information. "
1671 "Maybe the email hasn't been generated by Claws Mail."));
1673 mailval = -1;
1676 if (fseek(fp, filepos, SEEK_SET) < 0) {
1677 FILE_OP_ERROR(file, "fseek");
1678 mailval = -1;
1681 if (newsgroup_list && newsac && (mailval == 0)) {
1682 Folder *folder;
1683 gchar *tmp = NULL;
1684 gchar buf[BUFFSIZE];
1685 FILE *tmpfp;
1687 /* write to temporary file */
1688 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1689 G_DIR_SEPARATOR, file);
1690 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1691 FILE_OP_ERROR(tmp, "fopen");
1692 newsval = -1;
1693 alertpanel_error(_("Couldn't create temporary file for news sending."));
1694 } else {
1695 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1696 FILE_OP_ERROR(tmp, "chmod");
1697 g_warning("can't change file mode");
1700 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1701 if (fputs(buf, tmpfp) == EOF) {
1702 FILE_OP_ERROR(tmp, "fputs");
1703 newsval = -1;
1704 if (errstr) {
1705 if (*errstr) g_free(*errstr);
1706 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1710 fclose(tmpfp);
1712 if (newsval == 0) {
1713 debug_print("Sending message by news\n");
1715 folder = FOLDER(newsac->folder);
1717 newsval = news_post(folder, tmp);
1718 if (newsval < 0 && errstr) {
1719 if (*errstr) g_free(*errstr);
1720 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1721 newsac->nntp_server);
1724 claws_unlink(tmp);
1726 g_free(tmp);
1729 fclose(fp);
1731 /* update session statistics */
1732 if (mailval == 0 && newsval == 0) {
1733 /* update session stats */
1734 if (replymessageid)
1735 session_stats.replied++;
1736 else if (fwdmessageid)
1737 session_stats.forwarded++;
1738 else
1739 session_stats.sent++;
1742 /* save message to outbox */
1743 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1744 debug_print("saving sent message...\n");
1746 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1747 outbox = folder_find_item_from_identifier(savecopyfolder);
1748 if (!outbox)
1749 outbox = folder_get_default_outbox();
1751 /* Mail was not saved to outbox before encrypting, save it now. */
1752 gboolean saved = FALSE;
1753 *queued_removed = FALSE;
1754 if (queue && msgnum > 0) {
1755 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1756 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1757 debug_print("moved queued mail %d to sent folder\n", msgnum);
1758 saved = TRUE;
1759 *queued_removed = TRUE;
1760 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1761 debug_print("copied queued mail %d to sent folder\n", msgnum);
1762 saved = TRUE;
1764 procmsg_msginfo_free(&queued_mail);
1766 if (!saved) {
1767 debug_print("resaving queued mail to sent folder\n");
1768 procmsg_save_to_outbox(outbox, file, TRUE);
1773 if (replymessageid != NULL || fwdmessageid != NULL) {
1774 gchar **tokens;
1775 FolderItem *item;
1777 if (replymessageid != NULL)
1778 tokens = g_strsplit(replymessageid, "\t", 0);
1779 else
1780 tokens = g_strsplit(fwdmessageid, "\t", 0);
1781 item = folder_find_item_from_identifier(tokens[0]);
1783 /* check if queued message has valid folder and message id */
1784 if (item != NULL && tokens[2] != NULL) {
1785 MsgInfo *msginfo;
1787 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1789 /* check if referring message exists and has a message id */
1790 if ((msginfo != NULL) &&
1791 (msginfo->msgid != NULL) &&
1792 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1793 procmsg_msginfo_free(&msginfo);
1794 msginfo = NULL;
1797 if (msginfo == NULL) {
1798 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1801 if (msginfo != NULL) {
1802 if (replymessageid != NULL) {
1803 MsgPermFlags to_unset = 0;
1805 if (prefs_common.mark_as_read_on_new_window)
1806 to_unset = (MSG_NEW|MSG_UNREAD);
1808 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1809 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1810 } else {
1811 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1813 procmsg_msginfo_free(&msginfo);
1816 g_strfreev(tokens);
1819 g_free(from);
1820 g_free(smtpserver);
1821 slist_free_strings_full(to_list);
1822 slist_free_strings_full(newsgroup_list);
1823 g_free(savecopyfolder);
1824 g_free(replymessageid);
1825 g_free(fwdmessageid);
1827 return (newsval != 0 ? newsval : mailval);
1830 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1832 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1833 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1834 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1835 return result;
1838 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1840 gint val;
1841 if (procmsg_queue_lock(errstr)) {
1842 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1843 procmsg_queue_unlock();
1844 return val;
1846 return -1;
1849 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1851 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1853 /* NEW flag */
1854 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1855 item->new_msgs++;
1858 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1859 item->new_msgs--;
1862 /* UNREAD flag */
1863 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1864 item->unread_msgs++;
1865 if (procmsg_msg_has_marked_parent(msginfo))
1866 item->unreadmarked_msgs++;
1869 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1870 item->unread_msgs--;
1871 if (procmsg_msg_has_marked_parent(msginfo))
1872 item->unreadmarked_msgs--;
1875 /* MARK flag */
1876 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1877 procmsg_update_unread_children(msginfo, TRUE);
1878 item->marked_msgs++;
1881 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1882 procmsg_update_unread_children(msginfo, FALSE);
1883 item->marked_msgs--;
1886 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1887 item->replied_msgs++;
1890 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1891 item->replied_msgs--;
1894 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1895 item->forwarded_msgs++;
1898 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1899 item->forwarded_msgs--;
1902 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1903 item->locked_msgs++;
1906 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1907 item->locked_msgs--;
1910 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1911 item->ignored_msgs--;
1914 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1915 item->ignored_msgs++;
1918 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1919 item->watched_msgs--;
1922 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1923 item->watched_msgs++;
1927 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1929 FolderItem *item;
1930 MsgInfoUpdate msginfo_update;
1931 MsgPermFlags perm_flags_new, perm_flags_old;
1932 MsgTmpFlags tmp_flags_old;
1934 cm_return_if_fail(msginfo != NULL);
1935 item = msginfo->folder;
1936 cm_return_if_fail(item != NULL);
1938 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1940 /* Perm Flags handling */
1941 perm_flags_old = msginfo->flags.perm_flags;
1942 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1943 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1944 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1946 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1947 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1950 if (perm_flags_old != perm_flags_new) {
1951 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1953 update_folder_msg_counts(item, msginfo, perm_flags_old);
1954 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1957 /* Tmp flags handling */
1958 tmp_flags_old = msginfo->flags.tmp_flags;
1959 msginfo->flags.tmp_flags |= tmp_flags;
1961 /* update notification */
1962 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1963 msginfo_update.msginfo = msginfo;
1964 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1965 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1966 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1970 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1972 FolderItem *item;
1973 MsgInfoUpdate msginfo_update;
1974 MsgPermFlags perm_flags_new, perm_flags_old;
1975 MsgTmpFlags tmp_flags_old;
1977 cm_return_if_fail(msginfo != NULL);
1978 item = msginfo->folder;
1979 cm_return_if_fail(item != NULL);
1981 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1983 /* Perm Flags handling */
1984 perm_flags_old = msginfo->flags.perm_flags;
1985 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1987 if (perm_flags_old != perm_flags_new) {
1988 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1990 update_folder_msg_counts(item, msginfo, perm_flags_old);
1993 /* Tmp flags hanlding */
1994 tmp_flags_old = msginfo->flags.tmp_flags;
1995 msginfo->flags.tmp_flags &= ~tmp_flags;
1997 /* update notification */
1998 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1999 msginfo_update.msginfo = msginfo;
2000 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2001 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2002 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2006 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2007 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2008 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2010 FolderItem *item;
2011 MsgInfoUpdate msginfo_update;
2012 MsgPermFlags perm_flags_new, perm_flags_old;
2013 MsgTmpFlags tmp_flags_old;
2015 cm_return_if_fail(msginfo != NULL);
2016 item = msginfo->folder;
2017 cm_return_if_fail(item != NULL);
2019 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2021 /* Perm Flags handling */
2022 perm_flags_old = msginfo->flags.perm_flags;
2023 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2024 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2025 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2027 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2028 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2031 if (perm_flags_old != perm_flags_new) {
2032 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2034 update_folder_msg_counts(item, msginfo, perm_flags_old);
2038 /* Tmp flags handling */
2039 tmp_flags_old = msginfo->flags.tmp_flags;
2040 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2041 msginfo->flags.tmp_flags |= add_tmp_flags;
2043 /* update notification */
2044 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2045 msginfo_update.msginfo = msginfo;
2046 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2047 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2048 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2053 *\brief check for flags (e.g. mark) in prior msgs of current thread
2055 *\param info Current message
2056 *\param perm_flags Flags to be checked
2057 *\param parentmsgs Hash of prior msgs to avoid loops
2059 *\return gboolean TRUE if perm_flags are found
2061 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2062 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2064 MsgInfo *tmp;
2066 cm_return_val_if_fail(info != NULL, FALSE);
2068 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2069 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2070 info->inreplyto);
2071 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2072 procmsg_msginfo_free(&tmp);
2073 return TRUE;
2074 } else if (tmp != NULL) {
2075 gboolean result;
2077 if (g_hash_table_lookup(parentmsgs, info)) {
2078 debug_print("loop detected: %d\n",
2079 info->msgnum);
2080 result = FALSE;
2081 } else {
2082 g_hash_table_insert(parentmsgs, info, "1");
2083 result = procmsg_msg_has_flagged_parent_real(
2084 tmp, perm_flags, parentmsgs);
2086 procmsg_msginfo_free(&tmp);
2087 return result;
2088 } else {
2089 return FALSE;
2091 } else
2092 return FALSE;
2096 *\brief Callback for cleaning up hash of parentmsgs
2098 static gboolean parentmsgs_hash_remove(gpointer key,
2099 gpointer value,
2100 gpointer user_data)
2102 return TRUE;
2106 *\brief Set up list of parentmsgs
2107 * See procmsg_msg_has_flagged_parent_real()
2109 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2111 gboolean result;
2112 static GHashTable *parentmsgs = NULL;
2114 if (parentmsgs == NULL)
2115 parentmsgs = g_hash_table_new(NULL, NULL);
2117 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2118 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2120 return result;
2124 *\brief Check if msgs prior in thread are marked
2125 * See procmsg_msg_has_flagged_parent_real()
2127 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2129 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2133 static GSList *procmsg_find_children_func(MsgInfo *info,
2134 GSList *children, GSList *all)
2136 GSList *cur;
2138 cm_return_val_if_fail(info!=NULL, children);
2139 if (info->msgid == NULL)
2140 return children;
2142 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2143 MsgInfo *tmp = (MsgInfo *)cur->data;
2144 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2145 /* Check if message is already in the list */
2146 if ((children == NULL) ||
2147 (g_slist_index(children, tmp) == -1)) {
2148 children = g_slist_prepend(children,
2149 procmsg_msginfo_new_ref(tmp));
2150 children = procmsg_find_children_func(tmp,
2151 children,
2152 all);
2156 return children;
2159 static GSList *procmsg_find_children (MsgInfo *info)
2161 GSList *children;
2162 GSList *all, *cur;
2164 cm_return_val_if_fail(info!=NULL, NULL);
2165 all = folder_item_get_msg_list(info->folder);
2166 children = procmsg_find_children_func(info, NULL, all);
2167 if (children != NULL) {
2168 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2169 /* this will not free the used pointers
2170 created with procmsg_msginfo_new_ref */
2171 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2174 g_slist_free(all);
2176 return children;
2179 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2181 GSList *children = procmsg_find_children(info);
2182 GSList *cur;
2183 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2184 MsgInfo *tmp = (MsgInfo *)cur->data;
2185 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2186 if(newly_marked)
2187 info->folder->unreadmarked_msgs++;
2188 else
2189 info->folder->unreadmarked_msgs--;
2190 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2192 procmsg_msginfo_free(&tmp);
2194 g_slist_free(children);
2198 * Set the destination folder for a copy or move operation
2200 * \param msginfo The message which's destination folder is changed
2201 * \param to_folder The destination folder for the operation
2203 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2205 if(msginfo->to_folder != NULL) {
2206 msginfo->to_folder->op_count--;
2207 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2209 msginfo->to_folder = to_folder;
2210 if(to_folder != NULL) {
2211 to_folder->op_count++;
2212 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2217 * Apply filtering actions to the msginfo
2219 * \param msginfo The MsgInfo describing the message that should be filtered
2220 * \return TRUE if the message was moved and MsgInfo is now invalid,
2221 * FALSE otherwise
2223 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2225 MailFilteringData mail_filtering_data;
2227 mail_filtering_data.msginfo = msginfo;
2228 mail_filtering_data.msglist = NULL;
2229 mail_filtering_data.filtered = NULL;
2230 mail_filtering_data.unfiltered = NULL;
2231 mail_filtering_data.account = ac_prefs;
2233 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2234 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2235 return TRUE;
2237 /* filter if enabled in prefs or move to inbox if not */
2238 if((filtering_rules != NULL) &&
2239 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2240 FILTERING_INCORPORATION, NULL)) {
2241 return TRUE;
2244 return FALSE;
2247 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2248 GSList **filtered, GSList **unfiltered,
2249 gboolean do_filter)
2251 GSList *cur, *to_do = NULL;
2252 gint total = 0, curnum = 0;
2253 MailFilteringData mail_filtering_data;
2255 cm_return_if_fail(filtered != NULL);
2256 cm_return_if_fail(unfiltered != NULL);
2258 *filtered = NULL;
2259 *unfiltered = NULL;
2261 if (list == NULL)
2262 return;
2264 total = g_slist_length(list);
2266 if (!do_filter) {
2267 *filtered = NULL;
2268 *unfiltered = g_slist_copy(list);
2269 return;
2272 statusbar_print_all(_("Filtering messages...\n"));
2274 mail_filtering_data.msginfo = NULL;
2275 mail_filtering_data.msglist = list;
2276 mail_filtering_data.filtered = NULL;
2277 mail_filtering_data.unfiltered = NULL;
2278 mail_filtering_data.account = ac;
2280 if (!ac || ac->filterhook_on_recv)
2281 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2283 if (mail_filtering_data.filtered == NULL &&
2284 mail_filtering_data.unfiltered == NULL) {
2285 /* nothing happened */
2286 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2287 to_do = list;
2289 if (mail_filtering_data.filtered != NULL) {
2290 /* keep track of what's been filtered by the hooks */
2291 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2292 g_slist_length(list),
2293 g_slist_length(mail_filtering_data.filtered),
2294 g_slist_length(mail_filtering_data.unfiltered));
2296 *filtered = g_slist_copy(mail_filtering_data.filtered);
2298 if (mail_filtering_data.unfiltered != NULL) {
2299 /* what the hooks didn't handle will go in filtered or
2300 * unfiltered in the next loop */
2301 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2302 g_slist_length(list),
2303 g_slist_length(mail_filtering_data.filtered),
2304 g_slist_length(mail_filtering_data.unfiltered));
2305 to_do = mail_filtering_data.unfiltered;
2308 for (cur = to_do; cur; cur = cur->next) {
2309 MsgInfo *info = (MsgInfo *)cur->data;
2310 if (procmsg_msginfo_filter(info, ac))
2311 *filtered = g_slist_prepend(*filtered, info);
2312 else
2313 *unfiltered = g_slist_prepend(*unfiltered, info);
2314 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2317 g_slist_free(mail_filtering_data.filtered);
2318 g_slist_free(mail_filtering_data.unfiltered);
2320 *filtered = g_slist_reverse(*filtered);
2321 *unfiltered = g_slist_reverse(*unfiltered);
2323 statusbar_progress_all(0,0,0);
2324 statusbar_pop_all();
2327 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2329 MsgInfo *tmp_msginfo = NULL;
2330 MsgFlags flags = {0, 0};
2331 gchar *tmpfile = get_tmp_file();
2332 FILE *fp = g_fopen(tmpfile, "wb");
2334 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2335 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2336 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2337 if (fp)
2338 fclose(fp);
2339 g_free(tmpfile);
2340 return NULL;
2343 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2344 fclose(fp);
2345 fp = NULL;
2346 tmp_msginfo = procheader_parse_file(
2347 tmpfile, flags,
2348 TRUE, FALSE);
2350 if (fp)
2351 fclose(fp);
2353 if (tmp_msginfo != NULL) {
2354 if (src_msginfo)
2355 tmp_msginfo->folder = src_msginfo->folder;
2356 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2357 } else {
2358 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2361 g_free(tmpfile);
2363 return tmp_msginfo;
2366 static GSList *spam_learners = NULL;
2368 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2370 if (!g_slist_find(spam_learners, learn_func))
2371 spam_learners = g_slist_append(spam_learners, learn_func);
2372 if (mainwindow_get_mainwindow()) {
2373 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2374 summary_set_menu_sensitive(
2375 mainwindow_get_mainwindow()->summaryview);
2376 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2380 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2382 spam_learners = g_slist_remove(spam_learners, learn_func);
2383 if (mainwindow_get_mainwindow()) {
2384 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2385 summary_set_menu_sensitive(
2386 mainwindow_get_mainwindow()->summaryview);
2387 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2391 gboolean procmsg_spam_can_learn(void)
2393 return g_slist_length(spam_learners) > 0;
2396 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2398 GSList *cur = spam_learners;
2399 int ret = 0;
2400 for (; cur; cur = cur->next) {
2401 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2402 ret |= func(info, list, spam);
2404 return ret;
2407 static gchar *spam_folder_item = NULL;
2408 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2409 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2411 g_free(spam_folder_item);
2412 if (item_identifier)
2413 spam_folder_item = g_strdup(item_identifier);
2414 else
2415 spam_folder_item = NULL;
2416 if (spam_get_folder_func != NULL)
2417 procmsg_spam_get_folder_func = spam_get_folder_func;
2418 else
2419 procmsg_spam_get_folder_func = NULL;
2422 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2424 FolderItem *item = NULL;
2426 if (procmsg_spam_get_folder_func)
2427 item = procmsg_spam_get_folder_func(msginfo);
2428 if (item == NULL && spam_folder_item)
2429 item = folder_find_item_from_identifier(spam_folder_item);
2430 if (item == NULL)
2431 item = folder_get_default_trash();
2432 return item;
2435 static void item_has_queued_mails(FolderItem *item, gpointer data)
2437 gboolean *result = (gboolean *)data;
2438 if (*result == TRUE)
2439 return;
2440 if (folder_has_parent_of_type(item, F_QUEUE)) {
2441 if (item->total_msgs == 0)
2442 return;
2443 else {
2444 GSList *msglist = folder_item_get_msg_list(item);
2445 GSList *cur;
2446 for (cur = msglist; cur; cur = cur->next) {
2447 MsgInfo *msginfo = (MsgInfo *)cur->data;
2448 if (!MSG_IS_DELETED(msginfo->flags) &&
2449 !MSG_IS_LOCKED(msginfo->flags)) {
2450 *result = TRUE;
2451 break;
2454 procmsg_msg_list_free(msglist);
2459 gboolean procmsg_have_queued_mails_fast (void)
2461 gboolean result = FALSE;
2462 folder_func_to_all_folders(item_has_queued_mails, &result);
2463 return result;
2466 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2468 gboolean *result = (gboolean *)data;
2469 if (*result == TRUE)
2470 return;
2471 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2472 *result = TRUE;
2475 gboolean procmsg_have_trashed_mails_fast (void)
2477 gboolean result = FALSE;
2478 folder_func_to_all_folders(item_has_trashed_mails, &result);
2479 return result;
2482 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2484 GSList *cur = NULL;
2485 gchar *tags = NULL;
2487 if (!msginfo)
2488 return NULL;
2490 if (msginfo->tags == NULL)
2491 return NULL;
2492 for (cur = msginfo->tags; cur; cur = cur->next) {
2493 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2494 if (!tag)
2495 continue;
2496 if (!tags)
2497 tags = g_strdup(tag);
2498 else {
2499 int olen = strlen(tags);
2500 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2501 tags = g_realloc(tags, nlen+1);
2502 if (!tags)
2503 return NULL;
2504 strcpy(tags+olen, ", ");
2505 strcpy(tags+olen+2, tag);
2506 tags[nlen]='\0';
2509 return tags;
2512 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2514 GSList changed;
2516 if (id == 0)
2517 return;
2519 if (!set) {
2520 msginfo->tags = g_slist_remove(
2521 msginfo->tags,
2522 GINT_TO_POINTER(id));
2523 changed.data = GINT_TO_POINTER(id);
2524 changed.next = NULL;
2525 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2526 } else {
2527 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2528 msginfo->tags = g_slist_append(
2529 msginfo->tags,
2530 GINT_TO_POINTER(id));
2532 changed.data = GINT_TO_POINTER(id);
2533 changed.next = NULL;
2534 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2539 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2541 GSList *unset = msginfo->tags;
2542 msginfo->tags = NULL;
2543 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2544 g_slist_free(unset);