sync with 0.8.11cvs27
[claws.git] / src / procmsg.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4  *
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 2 of the License, or
8  * (at your option) any later version.
9  *
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.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "intl.h"
27 #include "main.h"
28 #include "utils.h"
29 #include "procmsg.h"
30 #include "procheader.h"
31 #include "send_message.h"
32 #include "procmime.h"
33 #include "statusbar.h"
34 #include "prefs_filtering.h"
35 #include "filtering.h"
36 #include "folder.h"
37 #include "prefs_common.h"
38 #include "account.h"
39 #if USE_GPGME
40 #  include "rfc2015.h"
41 #endif
42 #include "alertpanel.h"
43 #include "news.h"
44 #include "hooks.h"
45 #include "msgcache.h"
46
47 typedef struct _FlagInfo        FlagInfo;
48
49 struct _FlagInfo
50 {
51         guint    msgnum;
52         MsgFlags flags;
53 };
54
55 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
56 {
57         GHashTable *msg_table;
58
59         if (mlist == NULL) return NULL;
60
61         msg_table = g_hash_table_new(NULL, g_direct_equal);
62         procmsg_msg_hash_table_append(msg_table, mlist);
63
64         return msg_table;
65 }
66
67 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
68 {
69         GSList *cur;
70         MsgInfo *msginfo;
71
72         if (msg_table == NULL || mlist == NULL) return;
73
74         for (cur = mlist; cur != NULL; cur = cur->next) {
75                 msginfo = (MsgInfo *)cur->data;
76
77                 g_hash_table_insert(msg_table,
78                                     GUINT_TO_POINTER(msginfo->msgnum),
79                                     msginfo);
80         }
81 }
82
83 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
84 {
85         GHashTable *msg_table;
86         GSList *cur;
87         MsgInfo *msginfo;
88
89         if (mlist == NULL) return NULL;
90
91         msg_table = g_hash_table_new(NULL, g_direct_equal);
92
93         for (cur = mlist; cur != NULL; cur = cur->next) {
94                 msginfo = (MsgInfo *)cur->data;
95                 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
96         }
97
98         return msg_table;
99 }
100
101 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
102 {
103         GSList *cur;
104         MsgInfo *msginfo;
105         gint last = 0;
106
107         for (cur = mlist; cur != NULL; cur = cur->next) {
108                 msginfo = (MsgInfo *)cur->data;
109                 if (msginfo && msginfo->msgnum > last)
110                         last = msginfo->msgnum;
111         }
112
113         return last;
114 }
115
116 void procmsg_msg_list_free(GSList *mlist)
117 {
118         GSList *cur;
119         MsgInfo *msginfo;
120
121         for (cur = mlist; cur != NULL; cur = cur->next) {
122                 msginfo = (MsgInfo *)cur->data;
123                 procmsg_msginfo_free(msginfo);
124         }
125         g_slist_free(mlist);
126 }
127
128 struct MarkSum {
129         gint *new_msgs;
130         gint *unread_msgs;
131         gint *total_msgs;
132         gint *min;
133         gint *max;
134         gint first;
135 };
136
137 static gboolean procmsg_ignore_node(GNode *node, gpointer data)
138 {
139         MsgInfo *msginfo = (MsgInfo *)node->data;
140         
141         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
142         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
143
144         return FALSE;
145 }
146
147 /* return the reversed thread tree */
148 GNode *procmsg_get_thread_tree(GSList *mlist)
149 {
150         GNode *root, *parent, *node, *next;
151         GHashTable *msgid_table;
152         GHashTable *subject_table;
153         MsgInfo *msginfo;
154         const gchar *msgid;
155         const gchar *subject;
156         GNode *found_subject;
157
158         root = g_node_new(NULL);
159         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
160         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
161
162         for (; mlist != NULL; mlist = mlist->next) {
163                 msginfo = (MsgInfo *)mlist->data;
164                 parent = root;
165
166                 if (msginfo->inreplyto) {
167                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
168                         if (parent == NULL) {
169                                 parent = root;
170                         } else {
171                                 if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
172                                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
173                                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
174                                 }
175                         }
176                 }
177                 node = g_node_insert_data_before
178                         (parent, parent == root ? parent->children : NULL,
179                          msginfo);
180                 if ((msgid = msginfo->msgid) &&
181                     g_hash_table_lookup(msgid_table, msgid) == NULL)
182                         g_hash_table_insert(msgid_table, (gchar *)msgid, node);
183
184                 if (prefs_common.thread_by_subject) {
185                         subject = msginfo->subject;
186                         found_subject = subject_table_lookup(subject_table,
187                                                              (gchar *) subject);
188                         if (found_subject == NULL)
189                                 subject_table_insert(subject_table, (gchar *) subject,
190                                                      node);
191                         else {
192                                 /* replace if msg in table is older than current one 
193                                  * can add here more stuff. */
194                                 if ( ((MsgInfo*)(found_subject->data))->date_t >
195                                      ((MsgInfo*)(node->data))->date_t )  {
196                                         subject_table_remove(subject_table, (gchar *) subject);
197                                         subject_table_insert(subject_table, (gchar *) subject, node);
198                                 }       
199                         }
200                 }
201         }
202
203         /* complete the unfinished threads */
204         for (node = root->children; node != NULL; ) {
205                 next = node->next;
206                 msginfo = (MsgInfo *)node->data;
207                 parent = NULL;
208                 if (msginfo->inreplyto) 
209                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
210                 if (parent && parent != node) {
211                         g_node_unlink(node);
212                         g_node_insert_before
213                                 (parent, parent->children, node);
214                         /* CLAWS: ignore thread */
215                         if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
216                                 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, procmsg_ignore_node, NULL);
217                         }
218                 }
219                 node = next;
220         }
221
222         /* CLAWS: now see if the first level (below root) still has some nodes that can be
223          * threaded by subject line. we need to handle this in a special way to prevent
224          * circular reference from a node that has already been threaded by IN-REPLY-TO
225          * but is also in the subject line hash table */
226         if (prefs_common.thread_by_subject) {
227                 for (node = root->children; node != NULL; ) {
228                         next = node->next;
229                         msginfo = (MsgInfo *) node->data;
230                         parent = NULL;
231                         if (subject_is_reply(msginfo->subject)) {
232                                 parent = subject_table_lookup(subject_table,
233                                                               msginfo->subject);
234                                 /* the node may already be threaded by IN-REPLY-TO,
235                                    so go up in the tree to find the parent node */
236                                 if (parent != NULL) {
237                                         if (g_node_is_ancestor(node, parent))
238                                                 parent = NULL;
239                                         if (parent == node)
240                                                 parent = NULL;
241                                 }
242
243                                 if (parent) {
244                                         g_node_unlink(node);
245                                         g_node_append(parent, node);
246                                         /* CLAWS: ignore thread */
247                                         if (MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
248                                                 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, procmsg_ignore_node, NULL);
249                                         }
250                                 }
251                         }                                       
252                         node = next;
253                 }       
254         }
255         
256         g_hash_table_destroy(subject_table);
257         g_hash_table_destroy(msgid_table);
258
259         return root;
260 }
261
262 void procmsg_move_messages(GSList *mlist)
263 {
264         GSList *cur, *movelist = NULL;
265         MsgInfo *msginfo;
266         FolderItem *dest = NULL;
267
268         if (!mlist) return;
269
270         folder_item_update_freeze();
271
272         for (cur = mlist; cur != NULL; cur = cur->next) {
273                 msginfo = (MsgInfo *)cur->data;
274                 if (!dest) {
275                         dest = msginfo->to_folder;
276                         movelist = g_slist_append(movelist, msginfo);
277                 } else if (dest == msginfo->to_folder) {
278                         movelist = g_slist_append(movelist, msginfo);
279                 } else {
280                         folder_item_move_msgs_with_dest(dest, movelist);
281                         g_slist_free(movelist);
282                         movelist = NULL;
283                         dest = msginfo->to_folder;
284                         movelist = g_slist_append(movelist, msginfo);
285                 }
286                 procmsg_msginfo_set_to_folder(msginfo, NULL);
287         }
288
289         if (movelist) {
290                 folder_item_move_msgs_with_dest(dest, movelist);
291                 g_slist_free(movelist);
292         }
293
294         folder_item_update_thaw();
295 }
296
297 void procmsg_copy_messages(GSList *mlist)
298 {
299         GSList *cur, *copylist = NULL;
300         MsgInfo *msginfo;
301         FolderItem *dest = NULL;
302
303         if (!mlist) return;
304
305         folder_item_update_freeze();
306
307         for (cur = mlist; cur != NULL; cur = cur->next) {
308                 msginfo = (MsgInfo *)cur->data;
309                 if (!dest) {
310                         dest = msginfo->to_folder;
311                         copylist = g_slist_append(copylist, msginfo);
312                 } else if (dest == msginfo->to_folder) {
313                         copylist = g_slist_append(copylist, msginfo);
314                 } else {
315                         folder_item_copy_msgs_with_dest(dest, copylist);
316                         g_slist_free(copylist);
317                         copylist = NULL;
318                         dest = msginfo->to_folder;
319                         copylist = g_slist_append(copylist, msginfo);
320                 }
321                 procmsg_msginfo_set_to_folder(msginfo, NULL);
322         }
323
324         if (copylist) {
325                 folder_item_copy_msgs_with_dest(dest, copylist);
326                 g_slist_free(copylist);
327         }
328
329         folder_item_update_thaw();
330 }
331
332 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
333 {
334         gchar *path, *file;
335
336         g_return_val_if_fail(msginfo != NULL, NULL);
337
338         if (msginfo->plaintext_file)
339                 file = g_strdup(msginfo->plaintext_file);
340         else {
341                 path = folder_item_get_path(msginfo->folder);
342                 file = g_strconcat(path, G_DIR_SEPARATOR_S,
343                                    itos(msginfo->msgnum), NULL);
344                 g_free(path);
345         }
346
347         return file;
348 }
349
350 gchar *procmsg_get_message_file(MsgInfo *msginfo)
351 {
352         gchar *filename = NULL;
353
354         g_return_val_if_fail(msginfo != NULL, NULL);
355
356         filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
357         if (!filename)
358                 g_warning("can't fetch message %d\n", msginfo->msgnum);
359
360         return filename;
361 }
362
363 FILE *procmsg_open_message(MsgInfo *msginfo)
364 {
365         FILE *fp;
366         gchar *file;
367
368         g_return_val_if_fail(msginfo != NULL, NULL);
369
370         file = procmsg_get_message_file_path(msginfo);
371         g_return_val_if_fail(file != NULL, NULL);
372
373         if (!is_file_exist(file)) {
374                 g_free(file);
375                 file = procmsg_get_message_file(msginfo);
376                 g_return_val_if_fail(file != NULL, NULL);
377         }
378
379         if ((fp = fopen(file, "rb")) == NULL) {
380                 FILE_OP_ERROR(file, "fopen");
381                 g_free(file);
382                 return NULL;
383         }
384
385         g_free(file);
386
387         if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
388                 gchar buf[BUFFSIZE];
389
390                 while (fgets(buf, sizeof(buf), fp) != NULL)
391                         if (buf[0] == '\r' || buf[0] == '\n') break;
392         }
393
394         return fp;
395 }
396
397 #if USE_GPGME
398 FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
399 {
400         FILE *fp;
401         MimeInfo *mimeinfo_;
402
403         g_return_val_if_fail(msginfo != NULL, NULL);
404
405         if (mimeinfo) *mimeinfo = NULL;
406
407         if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL;
408
409         mimeinfo_ = procmime_scan_mime_header(fp);
410         if (!mimeinfo_) {
411                 fclose(fp);
412                 return NULL;
413         }
414
415         if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
416             rfc2015_is_encrypted(mimeinfo_)) {
417                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
418         }
419
420         if (MSG_IS_ENCRYPTED(msginfo->flags) &&
421             (!msginfo->plaintext_file || msginfo->decryption_failed)) {
422                 rfc2015_decrypt_message(msginfo, mimeinfo_, fp);
423                 if (msginfo->plaintext_file &&
424                     !msginfo->decryption_failed) {
425                         fclose(fp);
426                         procmime_mimeinfo_free_all(mimeinfo_);
427                         if ((fp = procmsg_open_message(msginfo)) == NULL)
428                                 return NULL;
429                         mimeinfo_ = procmime_scan_mime_header(fp);
430                         if (!mimeinfo_) {
431                                 fclose(fp);
432                                 return NULL;
433                         }
434                 }
435         }
436
437         if (mimeinfo) *mimeinfo = mimeinfo_;
438         return fp;
439 }
440 #endif
441
442 gboolean procmsg_msg_exist(MsgInfo *msginfo)
443 {
444         gchar *path;
445         gboolean ret;
446
447         if (!msginfo) return FALSE;
448
449         path = folder_item_get_path(msginfo->folder);
450         change_dir(path);
451         ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
452         g_free(path);
453
454         return ret;
455 }
456
457 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
458                                 PrefsFilterType type)
459 {
460         static HeaderEntry hentry[] = {{"X-BeenThere:",    NULL, TRUE},
461                                        {"X-ML-Name:",      NULL, TRUE},
462                                        {"X-List:",         NULL, TRUE},
463                                        {"X-Mailing-list:", NULL, TRUE},
464                                        {"List-Id:",        NULL, TRUE},
465                                        {NULL,              NULL, FALSE}};
466
467         enum
468         {
469                 H_X_BEENTHERE    = 0,
470                 H_X_ML_NAME      = 1,
471                 H_X_LIST         = 2,
472                 H_X_MAILING_LIST = 3,
473                 H_LIST_ID        = 4
474         };
475
476         FILE *fp;
477
478         g_return_if_fail(msginfo != NULL);
479         g_return_if_fail(header != NULL);
480         g_return_if_fail(key != NULL);
481
482         *header = NULL;
483         *key = NULL;
484
485         switch (type) {
486         case FILTER_BY_NONE:
487                 return;
488         case FILTER_BY_AUTO:
489                 if ((fp = procmsg_open_message(msginfo)) == NULL)
490                         return;
491                 procheader_get_header_fields(fp, hentry);
492                 fclose(fp);
493
494                 if (hentry[H_X_BEENTHERE].body != NULL) {
495                         *header = g_strdup("header \"X-BeenThere\"");
496                         *key = hentry[H_X_BEENTHERE].body;
497                         hentry[H_X_BEENTHERE].body = NULL;
498                 } else if (hentry[H_X_ML_NAME].body != NULL) {
499                         *header = g_strdup("header \"X-ML-Name\"");
500                         *key = hentry[H_X_ML_NAME].body;
501                         hentry[H_X_ML_NAME].body = NULL;
502                 } else if (hentry[H_X_LIST].body != NULL) {
503                         *header = g_strdup("header \"X-List\"");
504                         *key = hentry[H_X_LIST].body;
505                         hentry[H_X_LIST].body = NULL;
506                 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
507                         *header = g_strdup("header \"X-Mailing-List\"");
508                         *key = hentry[H_X_MAILING_LIST].body;
509                         hentry[H_X_MAILING_LIST].body = NULL;
510                 } else if (hentry[H_LIST_ID].body != NULL) {
511                         *header = g_strdup("header \"List-Id\"");
512                         *key = hentry[H_LIST_ID].body;
513                         extract_list_id_str(*key);
514                         hentry[H_LIST_ID].body = NULL;
515                 } else if (msginfo->subject) {
516                         *header = g_strdup("subject");
517                         *key = g_strdup(msginfo->subject);
518                 }
519
520                 g_free(hentry[H_X_BEENTHERE].body);
521                 hentry[H_X_BEENTHERE].body = NULL;
522                 g_free(hentry[H_X_ML_NAME].body);
523                 hentry[H_X_ML_NAME].body = NULL;
524                 g_free(hentry[H_X_LIST].body);
525                 hentry[H_X_LIST].body = NULL;
526                 g_free(hentry[H_X_MAILING_LIST].body);
527                 hentry[H_X_MAILING_LIST].body = NULL;
528                 g_free(hentry[H_LIST_ID].body);
529                 hentry[H_LIST_ID].body = NULL;
530
531                 break;
532         case FILTER_BY_FROM:
533                 *header = g_strdup("from");
534                 *key = g_strdup(msginfo->from);
535                 break;
536         case FILTER_BY_TO:
537                 *header = g_strdup("to");
538                 *key = g_strdup(msginfo->to);
539                 break;
540         case FILTER_BY_SUBJECT:
541                 *header = g_strdup("subject");
542                 *key = g_strdup(msginfo->subject);
543                 break;
544         default:
545                 break;
546         }
547 }
548
549 void procmsg_empty_trash(void)
550 {
551         FolderItem *trash;
552         GList *cur;
553
554         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
555                 trash = FOLDER(cur->data)->trash;
556                 if (trash && trash->total_msgs > 0)
557                         folder_item_remove_all_msg(trash);
558         }
559 }
560
561 /*!
562  *\brief        Send messages in queue
563  *
564  *\param        queue Queue folder to process
565  *\param        save_msgs Unused
566  *
567  *\return       Number of messages sent, negative if an error occurred
568  *              positive if no error occurred
569  */
570 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
571 {
572         gint ret = 1, count = 0;
573         GSList *list, *elem;
574
575         if (!queue)
576                 queue = folder_get_default_queue();
577         g_return_val_if_fail(queue != NULL, -1);
578
579         folder_item_scan(queue);
580         list = folder_item_get_msg_list(queue);
581
582         for (elem = list; elem != NULL; elem = elem->next) {
583                 gchar *file;
584                 MsgInfo *msginfo;
585                 
586                 msginfo = (MsgInfo *)(elem->data);
587                 if (!MSG_IS_LOCKED(msginfo->flags)) {
588                         file = folder_item_fetch_msg(queue, msginfo->msgnum);
589                         if (file) {
590                                 if (procmsg_send_message_queue(file) < 0) {
591                                         g_warning("Sending queued message %d failed.\n", 
592                                                   msginfo->msgnum);
593                                         ret = -1;
594                                 } else {
595                                         /* CLAWS: 
596                                          * We save in procmsg_send_message_queue because
597                                          * we need the destination folder from the queue
598                                          * header
599                                                         
600                                         if (save_msgs)
601                                                 procmsg_save_to_outbox
602                                                         (queue->folder->outbox,
603                                                          file, TRUE);
604                                          */
605                                         count++; 
606                                         folder_item_remove_msg(queue, msginfo->msgnum);
607                                 }
608                                 g_free(file);
609                         }
610                 }
611                 /* FIXME: supposedly if only one message is locked, and queue
612                  * is being flushed, the following free says something like 
613                  * "freeing msg ## in folder (nil)". */
614                 procmsg_msginfo_free(msginfo);
615         }
616
617         return ret * count;
618 }
619
620 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
621 {
622         FILE *fp, *outfp;
623         gchar buf[BUFFSIZE];
624         
625         if ((fp = fopen(in, "rb")) == NULL) {
626                 FILE_OP_ERROR(in, "fopen");
627                 return -1;
628         }
629         if ((outfp = fopen(out, "wb")) == NULL) {
630                 FILE_OP_ERROR(out, "fopen");
631                 fclose(fp);
632                 return -1;
633         }
634         while (fgets(buf, sizeof(buf), fp) != NULL)
635                 if (buf[0] == '\r' || buf[0] == '\n') break;
636         while (fgets(buf, sizeof(buf), fp) != NULL)
637                 fputs(buf, outfp);
638         fclose(outfp);
639         fclose(fp);
640         return 0;
641
642 }
643 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
644                             gboolean is_queued)
645 {
646         gint num;
647         MsgInfo *msginfo;
648
649         debug_print("saving sent message...\n");
650
651         if (!outbox)
652                 outbox = folder_get_default_outbox();
653         g_return_val_if_fail(outbox != NULL, -1);
654
655         /* remove queueing headers */
656         if (is_queued) {
657                 gchar tmp[MAXPATHLEN + 1];
658
659                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
660                            get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
661                 
662                 if (procmsg_remove_special_headers(file, tmp) !=0)
663                         return -1;
664
665                 folder_item_scan(outbox);
666                 if ((num = folder_item_add_msg(outbox, tmp, TRUE)) < 0) {
667                         g_warning("can't save message\n");
668                         unlink(tmp);
669                         return -1;
670                 }
671         } else {
672                 folder_item_scan(outbox);
673                 if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
674                         g_warning("can't save message\n");
675                         return -1;
676                 }
677                 return -1;
678         }
679         msginfo = folder_item_get_msginfo(outbox, num);
680         if (msginfo != NULL) {
681             procmsg_msginfo_unset_flags(msginfo, ~0, 0);
682             procmsg_msginfo_free(msginfo);
683         }
684         folder_item_update(outbox, TRUE);
685
686         return 0;
687 }
688
689 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
690 {
691         static const gchar *def_cmd = "lpr %s";
692         static guint id = 0;
693         gchar *prtmp;
694         FILE *tmpfp, *prfp;
695         gchar buf[1024];
696         gchar *p;
697
698         g_return_if_fail(msginfo);
699
700         if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
701                 g_warning("Can't get text part\n");
702                 return;
703         }
704
705         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
706                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
707
708         if ((prfp = fopen(prtmp, "wb")) == NULL) {
709                 FILE_OP_ERROR(prtmp, "fopen");
710                 g_free(prtmp);
711                 fclose(tmpfp);
712                 return;
713         }
714
715         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
716         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
717         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
718         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
719         if (msginfo->newsgroups)
720                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
721         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
722         fputc('\n', prfp);
723
724         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
725                 fputs(buf, prfp);
726
727         fclose(prfp);
728         fclose(tmpfp);
729
730         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
731             !strchr(p + 2, '%'))
732                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
733         else {
734                 if (cmdline)
735                         g_warning("Print command line is invalid: `%s'\n",
736                                   cmdline);
737                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
738         }
739
740         g_free(prtmp);
741
742         g_strchomp(buf);
743         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
744         system(buf);
745 }
746
747 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
748 {
749         msginfo->refcnt++;
750         
751         return msginfo;
752 }
753
754 MsgInfo *procmsg_msginfo_new()
755 {
756         MsgInfo *newmsginfo;
757
758         newmsginfo = g_new0(MsgInfo, 1);
759         newmsginfo->refcnt = 1;
760         
761         return newmsginfo;
762 }
763
764 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
765 {
766         MsgInfo *newmsginfo;
767
768         if (msginfo == NULL) return NULL;
769
770         newmsginfo = g_new0(MsgInfo, 1);
771
772         newmsginfo->refcnt = 1;
773
774 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
775 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
776                         g_strdup(msginfo->mmb) : NULL
777
778         MEMBCOPY(msgnum);
779         MEMBCOPY(size);
780         MEMBCOPY(mtime);
781         MEMBCOPY(date_t);
782         MEMBCOPY(flags);
783
784         MEMBDUP(fromname);
785
786         MEMBDUP(date);
787         MEMBDUP(from);
788         MEMBDUP(to);
789         MEMBDUP(cc);
790         MEMBDUP(newsgroups);
791         MEMBDUP(subject);
792         MEMBDUP(msgid);
793         MEMBDUP(inreplyto);
794         MEMBDUP(xref);
795
796         MEMBCOPY(folder);
797         MEMBCOPY(to_folder);
798
799         MEMBDUP(xface);
800         MEMBDUP(dispositionnotificationto);
801         MEMBDUP(returnreceiptto);
802         MEMBDUP(references);
803
804         MEMBCOPY(score);
805         MEMBCOPY(threadscore);
806
807         return newmsginfo;
808 }
809
810 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
811 {
812         MsgInfo *full_msginfo;
813         gchar *file;
814
815         if (msginfo == NULL) return NULL;
816
817         file = procmsg_get_message_file(msginfo);
818         if (!file) {
819                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
820                 return NULL;
821         }
822
823         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
824         g_free(file);
825         if (!full_msginfo) return NULL;
826
827         full_msginfo->msgnum = msginfo->msgnum;
828         full_msginfo->size = msginfo->size;
829         full_msginfo->mtime = msginfo->mtime;
830         full_msginfo->folder = msginfo->folder;
831 #if USE_GPGME
832         full_msginfo->plaintext_file = g_strdup(msginfo->plaintext_file);
833         full_msginfo->decryption_failed = msginfo->decryption_failed;
834 #endif
835         procmsg_msginfo_set_to_folder(full_msginfo, msginfo->to_folder);
836
837         return full_msginfo;
838 }
839
840 void procmsg_msginfo_free(MsgInfo *msginfo)
841 {
842         if (msginfo == NULL) return;
843
844         msginfo->refcnt--;
845         if (msginfo->refcnt > 0)
846                 return;
847
848         debug_print("freeing msginfo %d in %s\n", msginfo->msgnum, msginfo->folder ? msginfo->folder->path : "(nil)");
849
850         if (msginfo->to_folder) {
851                 msginfo->to_folder->op_count--;
852                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
853         }
854
855         g_free(msginfo->fromspace);
856         g_free(msginfo->references);
857         g_free(msginfo->returnreceiptto);
858         g_free(msginfo->dispositionnotificationto);
859         g_free(msginfo->xface);
860
861         g_free(msginfo->fromname);
862
863         g_free(msginfo->date);
864         g_free(msginfo->from);
865         g_free(msginfo->to);
866         g_free(msginfo->cc);
867         g_free(msginfo->newsgroups);
868         g_free(msginfo->subject);
869         g_free(msginfo->msgid);
870         g_free(msginfo->inreplyto);
871         g_free(msginfo->xref);
872
873         g_free(msginfo);
874 }
875
876 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
877 {
878         guint memusage = 0;
879         
880         memusage += sizeof(MsgInfo);
881         if (msginfo->fromname)
882                 memusage += strlen(msginfo->fromname);
883         if (msginfo->date)
884                 memusage += strlen(msginfo->date);
885         if (msginfo->from)
886                 memusage += strlen(msginfo->from);
887         if (msginfo->to)
888                 memusage += strlen(msginfo->to);
889         if (msginfo->cc)
890                 memusage += strlen(msginfo->cc);
891         if (msginfo->newsgroups)
892                 memusage += strlen(msginfo->newsgroups);
893         if (msginfo->subject)
894                 memusage += strlen(msginfo->subject);
895         if (msginfo->msgid)
896                 memusage += strlen(msginfo->msgid);
897         if (msginfo->inreplyto)
898                 memusage += strlen(msginfo->inreplyto);
899         if (msginfo->xface)
900                 memusage += strlen(msginfo->xface);
901         if (msginfo->dispositionnotificationto)
902                 memusage += strlen(msginfo->dispositionnotificationto);
903         if (msginfo->returnreceiptto)
904                 memusage += strlen(msginfo->returnreceiptto);
905         if (msginfo->references)
906                 memusage += strlen(msginfo->references);
907         if (msginfo->fromspace)
908                 memusage += strlen(msginfo->fromspace);
909
910         return memusage;
911 }
912
913 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
914 {
915         const MsgInfo *msginfo1 = a;
916         const MsgInfo *msginfo2 = b;
917
918         if (!msginfo1)
919                 return -1;
920         if (!msginfo2)
921                 return -1;
922
923         return msginfo1->msgnum - msginfo2->msgnum;
924 }
925
926 enum
927 {
928         Q_SENDER           = 0,
929         Q_SMTPSERVER       = 1,
930         Q_RECIPIENTS       = 2,
931         Q_NEWSGROUPS       = 3,
932         Q_MAIL_ACCOUNT_ID  = 4,
933         Q_NEWS_ACCOUNT_ID  = 5,
934         Q_SAVE_COPY_FOLDER = 6,
935         Q_REPLY_MESSAGE_ID = 7,
936         Q_FWD_MESSAGE_ID   = 8
937 };
938
939 gint procmsg_send_message_queue(const gchar *file)
940 {
941         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
942                                        {"SSV:",  NULL, FALSE},
943                                        {"R:",    NULL, FALSE},
944                                        {"NG:",   NULL, FALSE},
945                                        {"MAID:", NULL, FALSE},
946                                        {"NAID:", NULL, FALSE},
947                                        {"SCF:",  NULL, FALSE},
948                                        {"RMID:", NULL, FALSE},
949                                        {"FMID:", NULL, FALSE},
950                                        {NULL,    NULL, FALSE}};
951         FILE *fp;
952         gint filepos;
953         gint mailval = 0, newsval = 0;
954         gchar *from = NULL;
955         gchar *smtpserver = NULL;
956         GSList *to_list = NULL;
957         GSList *newsgroup_list = NULL;
958         gchar *savecopyfolder = NULL;
959         gchar *replymessageid = NULL;
960         gchar *fwdmessageid = NULL;
961         gchar buf[BUFFSIZE];
962         gint hnum;
963         PrefsAccount *mailac = NULL, *newsac = NULL;
964         int local = 0;
965
966         g_return_val_if_fail(file != NULL, -1);
967
968         if ((fp = fopen(file, "rb")) == NULL) {
969                 FILE_OP_ERROR(file, "fopen");
970                 return -1;
971         }
972
973         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
974                != -1) {
975                 gchar *p = buf + strlen(qentry[hnum].name);
976
977                 switch (hnum) {
978                 case Q_SENDER:
979                         if (!from) from = g_strdup(p);
980                         break;
981                 case Q_SMTPSERVER:
982                         if (!smtpserver) smtpserver = g_strdup(p);
983                         break;
984                 case Q_RECIPIENTS:
985                         to_list = address_list_append(to_list, p);
986                         break;
987                 case Q_NEWSGROUPS:
988                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
989                         break;
990                 case Q_MAIL_ACCOUNT_ID:
991                         mailac = account_find_from_id(atoi(p));
992                         break;
993                 case Q_NEWS_ACCOUNT_ID:
994                         newsac = account_find_from_id(atoi(p));
995                         break;
996                 case Q_SAVE_COPY_FOLDER:
997                         if (!savecopyfolder) savecopyfolder = g_strdup(p);
998                         break;
999                 case Q_REPLY_MESSAGE_ID:
1000                         if (!replymessageid) replymessageid = g_strdup(p);
1001                         break;
1002                 case Q_FWD_MESSAGE_ID:
1003                         if (!fwdmessageid) fwdmessageid = g_strdup(p);
1004                         break;
1005                 }
1006         }
1007         filepos = ftell(fp);
1008
1009         if (to_list) {
1010                 debug_print("Sending message by mail\n");
1011                 if (!from) {
1012                         g_warning("Queued message header is broken.\n");
1013                         mailval = -1;
1014                 } else if (mailac && mailac->use_mail_command &&
1015                            mailac->mail_command && (* mailac->mail_command)) {
1016                         mailval = send_message_local(mailac->mail_command, fp);
1017                         local = 1;
1018                 } else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
1019                         mailval = send_message_local(prefs_common.extsend_cmd, fp);
1020                         local = 1;
1021                 } else {
1022                         if (!mailac) {
1023                                 mailac = account_find_from_smtp_server(from, smtpserver);
1024                                 if (!mailac) {
1025                                         g_warning("Account not found. "
1026                                                     "Using current account...\n");
1027                                         mailac = cur_account;
1028                                 }
1029                         }
1030
1031                         if (mailac)
1032                                 mailval = send_message_smtp(mailac, to_list, fp);
1033                         else {
1034                                 PrefsAccount tmp_ac;
1035
1036                                 g_warning("Account not found.\n");
1037
1038                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1039                                 tmp_ac.address = from;
1040                                 tmp_ac.smtp_server = smtpserver;
1041                                 tmp_ac.smtpport = SMTP_PORT;
1042                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1043                         }
1044                 }
1045                 if (mailval < 0) {
1046                         if (!local)
1047                                 alertpanel_error_log(
1048                                         _("Error occurred while sending the message to `%s'."),
1049                                         mailac ? mailac->smtp_server : smtpserver);
1050                         else
1051                                 alertpanel_error_log(
1052                                         _("Error occurred while sending the message with command `%s'."),
1053                                         (mailac && mailac->use_mail_command && 
1054                                          mailac->mail_command && (*mailac->mail_command)) ? 
1055                                                 mailac->mail_command : prefs_common.extsend_cmd);
1056                 }
1057         }
1058
1059         fseek(fp, filepos, SEEK_SET);
1060         if (newsgroup_list && (newsval == 0)) {
1061                 Folder *folder;
1062                 gchar *tmp = NULL;
1063                 FILE *tmpfp;
1064
1065                 /* write to temporary file */
1066                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1067                             G_DIR_SEPARATOR, (gint)file);
1068                 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1069                         FILE_OP_ERROR(tmp, "fopen");
1070                         newsval = -1;
1071                         alertpanel_error(_("Could not create temporary file for news sending."));
1072                 } else {
1073                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1074                                 FILE_OP_ERROR(tmp, "chmod");
1075                                 g_warning("can't change file mode\n");
1076                         }
1077
1078                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1079                                 if (fputs(buf, tmpfp) == EOF) {
1080                                         FILE_OP_ERROR(tmp, "fputs");
1081                                         newsval = -1;
1082                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1083                                 }
1084                         }
1085                         fclose(tmpfp);
1086
1087                         if (newsval == 0) {
1088                                 debug_print("Sending message by news\n");
1089
1090                                 folder = FOLDER(newsac->folder);
1091
1092                                 newsval = news_post(folder, tmp);
1093                                 if (newsval < 0) {
1094                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1095                                                  newsac->nntp_server);
1096                                 }
1097                         }
1098                         unlink(tmp);
1099                 }
1100                 g_free(tmp);
1101         }
1102
1103         slist_free_strings(to_list);
1104         g_slist_free(to_list);
1105         slist_free_strings(newsgroup_list);
1106         g_slist_free(newsgroup_list);
1107         g_free(from);
1108         g_free(smtpserver);
1109         fclose(fp);
1110
1111         /* save message to outbox */
1112         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1113                 FolderItem *outbox;
1114
1115                 debug_print("saving sent message...\n");
1116
1117                 outbox = folder_find_item_from_identifier(savecopyfolder);
1118                 if (!outbox)
1119                         outbox = folder_get_default_outbox();
1120
1121                 procmsg_save_to_outbox(outbox, file, TRUE);
1122         }
1123
1124         if (replymessageid != NULL || fwdmessageid != NULL) {
1125                 gchar **tokens;
1126                 FolderItem *item;
1127                 
1128                 if (replymessageid != NULL)
1129                         tokens = g_strsplit(replymessageid, "\x7f", 0);
1130                 else
1131                         tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1132                 item = folder_find_item_from_identifier(tokens[0]);
1133                 if (item != NULL) {
1134                         MsgInfo *msginfo;
1135                         
1136                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1137                         if ((msginfo != NULL) && (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1138                                 procmsg_msginfo_free(msginfo);
1139                                 msginfo = NULL;
1140                         }
1141                         
1142                         if (msginfo == NULL) {
1143                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1144                         }
1145                         
1146                         if (msginfo != NULL) {
1147                                 if (replymessageid != NULL) {
1148                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1149                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1150                                 } 
1151                                 else {
1152                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1153                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1154                                 }
1155                                 procmsg_msginfo_free(msginfo);
1156                         }
1157                 }
1158                 g_strfreev(tokens);
1159         }
1160
1161         g_free(savecopyfolder);
1162         g_free(replymessageid);
1163         g_free(fwdmessageid);
1164         
1165         return (newsval != 0 ? newsval : mailval);
1166 }
1167
1168 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1169 {
1170         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1171
1172         /* NEW flag */
1173         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1174                 item->new_msgs++;
1175         }
1176
1177         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1178                 item->new_msgs--;
1179         }
1180
1181         /* UNREAD flag */
1182         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1183                 item->unread_msgs++;
1184                 if (procmsg_msg_has_marked_parent(msginfo))
1185                         item->unreadmarked_msgs++;
1186         }
1187
1188         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1189                 item->unread_msgs--;
1190                 if (procmsg_msg_has_marked_parent(msginfo))
1191                         item->unreadmarked_msgs--;
1192         }
1193         
1194         /* MARK flag */
1195         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1196                 procmsg_update_unread_children(msginfo, TRUE);
1197         }
1198
1199         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1200                 procmsg_update_unread_children(msginfo, FALSE);
1201         }
1202 }
1203
1204 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1205 {
1206         FolderItem *item;
1207         MsgInfoUpdate msginfo_update;
1208         MsgPermFlags perm_flags_new, perm_flags_old;
1209
1210         g_return_if_fail(msginfo != NULL);
1211         item = msginfo->folder;
1212         g_return_if_fail(item != NULL);
1213         
1214         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1215
1216         /* Perm Flags handling */
1217         perm_flags_old = msginfo->flags.perm_flags;
1218         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1219         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1220                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1221         }
1222
1223         if (perm_flags_old != perm_flags_new) {
1224                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1225
1226                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1227
1228                 msginfo_update.msginfo = msginfo;
1229                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1230                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1231         }
1232
1233         /* Tmp flags hanlding */
1234         msginfo->flags.tmp_flags |= tmp_flags;
1235 }
1236
1237 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1238 {
1239         FolderItem *item;
1240         MsgInfoUpdate msginfo_update;
1241         MsgPermFlags perm_flags_new, perm_flags_old;
1242
1243         g_return_if_fail(msginfo != NULL);
1244         item = msginfo->folder;
1245         g_return_if_fail(item != NULL);
1246         
1247         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1248
1249         /* Perm Flags handling */
1250         perm_flags_old = msginfo->flags.perm_flags;
1251         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1252         
1253         if (perm_flags_old != perm_flags_new) {
1254                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1255
1256                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1257
1258                 msginfo_update.msginfo = msginfo;
1259                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1260                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1261         }
1262
1263         /* Tmp flags hanlding */
1264         msginfo->flags.tmp_flags &= ~tmp_flags;
1265 }
1266
1267 /*!
1268  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1269  *
1270  *\param        info Current message
1271  *\param        perm_flags Flags to be checked
1272  *\param        parentmsgs Hash of prior msgs to avoid loops
1273  *
1274  *\return       gboolean TRUE if perm_flags are found
1275  */
1276 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1277                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1278 {
1279         MsgInfo *tmp;
1280
1281         g_return_val_if_fail(info != NULL, FALSE);
1282
1283         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1284                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1285                                 info->inreplyto);
1286                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1287                         procmsg_msginfo_free(tmp);
1288                         return TRUE;
1289                 } else if (tmp != NULL) {
1290                         gboolean result;
1291
1292                         if (g_hash_table_lookup(parentmsgs, info)) {
1293                                 debug_print("loop detected: %s%c%d\n",
1294                                         folder_item_get_path(info->folder),
1295                                         G_DIR_SEPARATOR, info->msgnum);
1296                                 result = FALSE;
1297                         } else {
1298                                 g_hash_table_insert(parentmsgs, info, "1");
1299                                 result = procmsg_msg_has_flagged_parent_real(
1300                                     tmp, perm_flags, parentmsgs);
1301                         }
1302                         procmsg_msginfo_free(tmp);
1303                         return result;
1304                 } else {
1305                         return FALSE;
1306                 }
1307         } else
1308                 return FALSE;
1309 }
1310
1311 /*!
1312  *\brief        Callback for cleaning up hash of parentmsgs
1313  */
1314 gboolean parentmsgs_hash_remove(gpointer key,
1315                             gpointer value,
1316                             gpointer user_data)
1317 {
1318         return TRUE;
1319 }
1320
1321 /*!
1322  *\brief        Set up list of parentmsgs
1323  *              See procmsg_msg_has_flagged_parent_real()
1324  */
1325 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1326 {
1327         gboolean result;
1328         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1329
1330         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1331         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1332         g_hash_table_destroy(parentmsgs);
1333         return result;
1334 }
1335
1336 /*!
1337  *\brief        Check if msgs prior in thread are marked
1338  *              See procmsg_msg_has_flagged_parent_real()
1339  */
1340 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1341 {
1342         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1343 }
1344
1345
1346 GSList *procmsg_find_children_func(MsgInfo *info, 
1347                                    GSList *children, GSList *all)
1348 {
1349         GSList *cur;
1350
1351         g_return_val_if_fail(info!=NULL, children);
1352         if (info->msgid == NULL)
1353                 return children;
1354
1355         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1356                 MsgInfo *tmp = (MsgInfo *)cur->data;
1357                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1358                         /* Check if message is already in the list */
1359                         if ((children == NULL) || 
1360                             (g_slist_index(children, tmp) == -1)) {
1361                                 children = g_slist_prepend(children,
1362                                                 procmsg_msginfo_new_ref(tmp));
1363                                 children = procmsg_find_children_func(tmp, 
1364                                                         children, 
1365                                                         all);
1366                         }
1367                 }
1368         }
1369         return children;
1370 }
1371
1372 GSList *procmsg_find_children (MsgInfo *info)
1373 {
1374         GSList *children;
1375         GSList *all, *cur;
1376
1377         g_return_val_if_fail(info!=NULL, NULL);
1378         all = folder_item_get_msg_list(info->folder);
1379         children = procmsg_find_children_func(info, NULL, all);
1380         if (children != NULL) {
1381                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1382                         /* this will not free the used pointers
1383                            created with procmsg_msginfo_new_ref */
1384                         procmsg_msginfo_free((MsgInfo *)cur->data);
1385                 }
1386         }
1387         g_slist_free(all);
1388
1389         return children;
1390 }
1391
1392 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1393 {
1394         GSList *children = procmsg_find_children(info);
1395         GSList *cur;
1396         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1397                 MsgInfo *tmp = (MsgInfo *)cur->data;
1398                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1399                         if(newly_marked) 
1400                                 info->folder->unreadmarked_msgs++;
1401                         else
1402                                 info->folder->unreadmarked_msgs--;
1403                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1404                 }
1405                 procmsg_msginfo_free(tmp);
1406         }
1407         g_slist_free(children);
1408 }
1409
1410 /**
1411  * Set the destination folder for a copy or move operation
1412  *
1413  * \param msginfo The message which's destination folder is changed
1414  * \param to_folder The destination folder for the operation
1415  */
1416 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1417 {
1418         if(msginfo->to_folder != NULL) {
1419                 msginfo->to_folder->op_count--;
1420                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1421         }
1422         msginfo->to_folder = to_folder;
1423         if(to_folder != NULL) {
1424                 to_folder->op_count++;
1425                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1426         }
1427 }
1428
1429 /**
1430  * Apply filtering actions to the msginfo
1431  *
1432  * \param msginfo The MsgInfo describing the message that should be filtered
1433  * \return TRUE if the message was moved and MsgInfo is now invalid,
1434  *         FALSE otherwise
1435  */
1436 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1437 {
1438         MailFilteringData mail_filtering_data;
1439                         
1440         mail_filtering_data.msginfo = msginfo;                  
1441         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1442                 return TRUE;
1443
1444         /* filter if enabled in prefs or move to inbox if not */
1445         if((global_processing != NULL) &&
1446            filter_message_by_msginfo(global_processing, msginfo))
1447                 return TRUE;
1448
1449         return FALSE;
1450 }