2006-08-01 [wwp] 2.4.0cvs9
[claws.git] / src / procmsg.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27
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 "timing.h"
49
50 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr);
51
52 enum
53 {
54         Q_SENDER           = 0,
55         Q_SMTPSERVER       = 1,
56         Q_RECIPIENTS       = 2,
57         Q_NEWSGROUPS       = 3,
58         Q_MAIL_ACCOUNT_ID  = 4,
59         Q_NEWS_ACCOUNT_ID  = 5,
60         Q_SAVE_COPY_FOLDER = 6,
61         Q_REPLY_MESSAGE_ID = 7,
62         Q_FWD_MESSAGE_ID   = 8,
63         Q_PRIVACY_SYSTEM   = 9,
64         Q_ENCRYPT          = 10,
65         Q_ENCRYPT_DATA     = 11,
66         Q_SYLPHEED_HDRS    = 12,
67 };
68
69 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
70 {
71         GHashTable *msg_table;
72
73         if (mlist == NULL) return NULL;
74
75         msg_table = g_hash_table_new(NULL, g_direct_equal);
76         procmsg_msg_hash_table_append(msg_table, mlist);
77
78         return msg_table;
79 }
80
81 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
82 {
83         GSList *cur;
84         MsgInfo *msginfo;
85
86         if (msg_table == NULL || mlist == NULL) return;
87
88         for (cur = mlist; cur != NULL; cur = cur->next) {
89                 msginfo = (MsgInfo *)cur->data;
90
91                 g_hash_table_insert(msg_table,
92                                     GUINT_TO_POINTER(msginfo->msgnum),
93                                     msginfo);
94         }
95 }
96
97 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
98 {
99         GHashTable *msg_table;
100         GSList *cur;
101         MsgInfo *msginfo;
102
103         if (mlist == NULL) return NULL;
104
105         msg_table = g_hash_table_new(NULL, g_direct_equal);
106
107         for (cur = mlist; cur != NULL; cur = cur->next) {
108                 msginfo = (MsgInfo *)cur->data;
109                 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
110         }
111
112         return msg_table;
113 }
114
115 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
116 {
117         GSList *cur;
118         MsgInfo *msginfo;
119         gint last = 0;
120
121         for (cur = mlist; cur != NULL; cur = cur->next) {
122                 msginfo = (MsgInfo *)cur->data;
123                 if (msginfo && msginfo->msgnum > last)
124                         last = msginfo->msgnum;
125         }
126
127         return last;
128 }
129
130 void procmsg_msg_list_free(GSList *mlist)
131 {
132         GSList *cur;
133         MsgInfo *msginfo;
134
135         for (cur = mlist; cur != NULL; cur = cur->next) {
136                 msginfo = (MsgInfo *)cur->data;
137                 procmsg_msginfo_free(msginfo);
138         }
139         g_slist_free(mlist);
140 }
141
142 struct MarkSum {
143         gint *new_msgs;
144         gint *unread_msgs;
145         gint *total_msgs;
146         gint *min;
147         gint *max;
148         gint first;
149 };
150
151 /* CLAWS subject threading:
152   
153   in the first round it inserts subject lines in a 
154   relation (subject <-> node)
155
156   the second round finishes the threads by attaching
157   matching subject lines to the one found in the
158   relation. will use the oldest node with the same
159   subject that is not more then thread_by_subject_max_age
160   days old (see subject_relation_lookup)
161 */  
162
163 static void subject_relation_insert(GRelation *relation, GNode *node)
164 {
165         gchar *subject;
166         MsgInfo *msginfo;
167
168         g_return_if_fail(relation != NULL);
169         g_return_if_fail(node != NULL);
170         msginfo = (MsgInfo *) node->data;
171         g_return_if_fail(msginfo != NULL);
172
173         subject = msginfo->subject;
174         if (subject == NULL)
175                 return;
176         subject += subject_get_prefix_length(subject);
177
178         g_relation_insert(relation, subject, node);
179 }
180
181 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
182 {
183         gchar *subject;
184         GTuples *tuples;
185         GNode *node = NULL;
186         gint prefix_length;
187     
188         g_return_val_if_fail(relation != NULL, NULL);
189
190         subject = msginfo->subject;
191         if (subject == NULL)
192                 return NULL;
193         prefix_length = subject_get_prefix_length(subject);
194         if (prefix_length <= 0)
195                 return NULL;
196         subject += prefix_length;
197         
198         tuples = g_relation_select(relation, subject, 0);
199         if (tuples == NULL)
200                 return NULL;
201
202         if (tuples->len > 0) {
203                 int i;
204                 GNode *relation_node;
205                 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
206                 gboolean match;
207
208                 /* check all nodes with the same subject to find the best parent */
209                 for (i = 0; i < tuples->len; i++) {
210                         relation_node = (GNode *) g_tuples_index(tuples, i, 1);
211                         relation_msginfo = (MsgInfo *) relation_node->data;
212                         match = FALSE;
213
214                         /* best node should be the oldest in the found nodes */
215                         /* parent node must not be older then msginfo */
216                         if ((relation_msginfo->date_t < msginfo->date_t) &&
217                             ((best_msginfo == NULL) ||
218                              (best_msginfo->date_t > relation_msginfo->date_t)))
219                                 match = TRUE;
220
221                         /* parent node must not be more then thread_by_subject_max_age
222                            days older then msginfo */
223                         if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
224                             prefs_common.thread_by_subject_max_age * 3600 * 24)
225                                 match = FALSE;
226
227                         /* can add new tests for all matching
228                            nodes found by subject */
229
230                         if (match) {
231                                 node = relation_node;
232                                 best_msginfo = relation_msginfo;
233                         }
234                 }           
235         }
236
237         g_tuples_destroy(tuples);
238         return node;
239 }
240
241 /* return the reversed thread tree */
242 GNode *procmsg_get_thread_tree(GSList *mlist)
243 {
244         GNode *root, *parent, *node, *next;
245         GHashTable *msgid_table;
246         GRelation *subject_relation;
247         MsgInfo *msginfo;
248         const gchar *msgid;
249         GSList *reflist;
250         START_TIMING("procmsg_get_thread_tree");
251         root = g_node_new(NULL);
252         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
253         subject_relation = g_relation_new(2);
254         g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
255
256         for (; mlist != NULL; mlist = mlist->next) {
257                 msginfo = (MsgInfo *)mlist->data;
258                 parent = root;
259
260                 if (msginfo->inreplyto) {
261                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
262                         if (parent == NULL) {
263                                 parent = root;
264                         }
265                 }
266                 node = g_node_insert_data_before
267                         (parent, parent == root ? parent->children : NULL,
268                          msginfo);
269                 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
270                         g_hash_table_insert(msgid_table, (gchar *)msgid, node);
271
272                 /* CLAWS: add subject to relation (without prefix) */
273                 if (prefs_common.thread_by_subject) {
274                         subject_relation_insert(subject_relation, node);
275                 }
276         }
277
278         /* complete the unfinished threads */
279         for (node = root->children; node != NULL; ) {
280                 next = node->next;
281                 msginfo = (MsgInfo *)node->data;
282                 parent = NULL;
283                 
284                 if (msginfo->inreplyto)
285                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
286
287                 /* try looking for the indirect parent */
288                 if (!parent && msginfo->references) {
289                         for (reflist = msginfo->references;
290                              reflist != NULL; reflist = reflist->next)
291                                 if ((parent = g_hash_table_lookup
292                                         (msgid_table, reflist->data)) != NULL)
293                                         break;
294                 }                                        
295               
296                 /* node should not be the parent, and node should not
297                    be an ancestor of parent (circular reference) */
298                 if (parent && parent != node &&
299                     !g_node_is_ancestor(node, parent)) {
300                         g_node_unlink(node);
301                         g_node_insert_before
302                                 (parent, parent->children, node);
303                 }
304                
305                 node = next;
306         }
307
308         if (prefs_common.thread_by_subject) {
309                 START_TIMING("procmsg_get_thread_tree(1)");
310                 for (node = root->children; node && node != NULL;) {
311                         next = node->next;
312                         msginfo = (MsgInfo *) node->data;
313                         
314                         parent = subject_relation_lookup(subject_relation, msginfo);
315                         
316                         /* the node may already be threaded by IN-REPLY-TO, so go up 
317                          * in the tree to 
318                            find the parent node */
319                         if (parent != NULL) {
320                                 if (g_node_is_ancestor(node, parent))
321                                         parent = NULL;
322                                 if (parent == node)
323                                         parent = NULL;
324                         }
325                         
326                         if (parent) {
327                                 g_node_unlink(node);
328                                 g_node_append(parent, node);
329                         }
330
331                         node = next;
332                 }       
333                 END_TIMING();
334         }
335         
336         g_relation_destroy(subject_relation);
337         g_hash_table_destroy(msgid_table);
338         END_TIMING();
339         return root;
340 }
341
342 gint procmsg_move_messages(GSList *mlist)
343 {
344         GSList *cur, *movelist = NULL;
345         MsgInfo *msginfo;
346         FolderItem *dest = NULL;
347         gint retval = 0;
348         gboolean finished = TRUE;
349         if (!mlist) return 0;
350
351         folder_item_update_freeze();
352
353 next_folder:
354         for (cur = mlist; cur != NULL; cur = cur->next) {
355                 msginfo = (MsgInfo *)cur->data;
356                 if (!msginfo->to_folder) {
357                         continue;
358                 } else {
359                         finished = FALSE;
360                 }
361                 if (!dest) {
362                         dest = msginfo->to_folder;
363                         movelist = g_slist_prepend(movelist, msginfo);
364                 } else if (dest == msginfo->to_folder) {
365                         movelist = g_slist_prepend(movelist, msginfo);
366                 } else {
367                         continue;
368                 }
369                 procmsg_msginfo_set_to_folder(msginfo, NULL);
370         }
371         if (movelist) {
372                 movelist = g_slist_reverse(movelist);
373                 retval |= folder_item_move_msgs(dest, movelist);
374                 g_slist_free(movelist);
375                 movelist = NULL;
376         }
377         if (finished == FALSE) {
378                 finished = TRUE;
379                 dest = NULL;
380                 goto next_folder;
381         }
382
383         folder_item_update_thaw();
384         return retval;
385 }
386
387 void procmsg_copy_messages(GSList *mlist)
388 {
389         GSList *cur, *copylist = NULL;
390         MsgInfo *msginfo;
391         FolderItem *dest = NULL;
392         gboolean finished = TRUE;
393         if (!mlist) return;
394
395         folder_item_update_freeze();
396
397 next_folder:
398         for (cur = mlist; cur != NULL; cur = cur->next) {
399                 msginfo = (MsgInfo *)cur->data;
400                 if (!msginfo->to_folder) {
401                         continue;
402                 } else {
403                         finished = FALSE;
404                 }
405                 if (!dest) {
406                         dest = msginfo->to_folder;
407                         copylist = g_slist_prepend(copylist, msginfo);
408                 } else if (dest == msginfo->to_folder) {
409                         copylist = g_slist_prepend(copylist, msginfo);
410                 } else {
411                         continue;
412                 }
413                 procmsg_msginfo_set_to_folder(msginfo, NULL);
414         }
415         if (copylist) {
416                 copylist = g_slist_reverse(copylist);
417                 folder_item_copy_msgs(dest, copylist);
418                 g_slist_free(copylist);
419                 copylist = NULL;
420         }
421         if (finished == FALSE) {
422                 finished = TRUE;
423                 dest = NULL;
424                 goto next_folder;
425         }
426
427         folder_item_update_thaw();
428 }
429
430 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
431 {
432         gchar *file;
433
434         g_return_val_if_fail(msginfo != NULL, NULL);
435
436         if (msginfo->plaintext_file)
437                 file = g_strdup(msginfo->plaintext_file);
438         else {
439                 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
440         }
441
442         return file;
443 }
444
445 gchar *procmsg_get_message_file(MsgInfo *msginfo)
446 {
447         gchar *filename = NULL;
448
449         g_return_val_if_fail(msginfo != NULL, NULL);
450
451         filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
452         if (!filename)
453                 debug_print("can't fetch message %d\n", msginfo->msgnum);
454
455         return filename;
456 }
457
458 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
459 {
460         gchar *filename = NULL;
461
462         g_return_val_if_fail(msginfo != NULL, NULL);
463
464         filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
465                                                 headers, body);
466         if (!filename)
467                 debug_print("can't fetch message %d\n", msginfo->msgnum);
468
469         return filename;
470 }
471
472 GSList *procmsg_get_message_file_list(GSList *mlist)
473 {
474         GSList *file_list = NULL;
475         MsgInfo *msginfo;
476         MsgFileInfo *fileinfo;
477         gchar *file;
478
479         while (mlist != NULL) {
480                 msginfo = (MsgInfo *)mlist->data;
481                 file = procmsg_get_message_file(msginfo);
482                 if (!file) {
483                         procmsg_message_file_list_free(file_list);
484                         return NULL;
485                 }
486                 fileinfo = g_new(MsgFileInfo, 1);
487                 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
488                 fileinfo->file = file;
489                 fileinfo->flags = g_new(MsgFlags, 1);
490                 *fileinfo->flags = msginfo->flags;
491                 file_list = g_slist_prepend(file_list, fileinfo);
492                 mlist = mlist->next;
493         }
494
495         file_list = g_slist_reverse(file_list);
496
497         return file_list;
498 }
499
500 void procmsg_message_file_list_free(MsgInfoList *file_list)
501 {
502         GSList *cur;
503         MsgFileInfo *fileinfo;
504
505         for (cur = file_list; cur != NULL; cur = cur->next) {
506                 fileinfo = (MsgFileInfo *)cur->data;
507                 procmsg_msginfo_free(fileinfo->msginfo);
508                 g_free(fileinfo->file);
509                 g_free(fileinfo->flags);
510                 g_free(fileinfo);
511         }
512
513         g_slist_free(file_list);
514 }
515
516 FILE *procmsg_open_message(MsgInfo *msginfo)
517 {
518         FILE *fp;
519         gchar *file;
520
521         g_return_val_if_fail(msginfo != NULL, NULL);
522
523         file = procmsg_get_message_file_path(msginfo);
524         g_return_val_if_fail(file != NULL, NULL);
525
526         if (!is_file_exist(file)) {
527                 g_free(file);
528                 file = procmsg_get_message_file(msginfo);
529                 if (!file)
530                         return NULL;
531         }
532
533         if ((fp = g_fopen(file, "rb")) == NULL) {
534                 FILE_OP_ERROR(file, "fopen");
535                 g_free(file);
536                 return NULL;
537         }
538
539         g_free(file);
540
541         if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
542                 gchar buf[BUFFSIZE];
543
544                 while (fgets(buf, sizeof(buf), fp) != NULL) {
545                         /* new way */
546                         if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
547                                 strlen("X-Sylpheed-End-Special-Headers:")))
548                                 break;
549                         /* old way */
550                         if (buf[0] == '\r' || buf[0] == '\n') break;
551                         /* from other mailers */
552                         if (!strncmp(buf, "Date: ", 6)
553                         ||  !strncmp(buf, "To: ", 4)
554                         ||  !strncmp(buf, "From: ", 6)
555                         ||  !strncmp(buf, "Subject: ", 9)) {
556                                 rewind(fp);
557                                 break;
558                         }
559                 }
560         }
561
562         return fp;
563 }
564
565 gboolean procmsg_msg_exist(MsgInfo *msginfo)
566 {
567         gchar *path;
568         gboolean ret;
569
570         if (!msginfo) return FALSE;
571
572         path = folder_item_get_path(msginfo->folder);
573         change_dir(path);
574         ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
575         g_free(path);
576
577         return ret;
578 }
579
580 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
581                                 PrefsFilterType type)
582 {
583         static HeaderEntry hentry[] = {{"X-BeenThere:",    NULL, TRUE},
584                                        {"X-ML-Name:",      NULL, TRUE},
585                                        {"X-List:",         NULL, TRUE},
586                                        {"X-Mailing-list:", NULL, TRUE},
587                                        {"List-Id:",        NULL, TRUE},
588                                        {"X-Sequence:",     NULL, TRUE},
589                                        {"Sender:",         NULL, TRUE},
590                                        {"List-Post:",      NULL, TRUE},
591                                        {NULL,              NULL, FALSE}};
592         enum
593         {
594                 H_X_BEENTHERE    = 0,
595                 H_X_ML_NAME      = 1,
596                 H_X_LIST         = 2,
597                 H_X_MAILING_LIST = 3,
598                 H_LIST_ID        = 4,
599                 H_X_SEQUENCE     = 5,
600                 H_SENDER         = 6,
601                 H_LIST_POST      = 7
602         };
603
604         FILE *fp;
605
606         g_return_if_fail(msginfo != NULL);
607         g_return_if_fail(header != NULL);
608         g_return_if_fail(key != NULL);
609
610         *header = NULL;
611         *key = NULL;
612
613         switch (type) {
614         case FILTER_BY_NONE:
615                 return;
616         case FILTER_BY_AUTO:
617                 if ((fp = procmsg_open_message(msginfo)) == NULL)
618                         return;
619                 procheader_get_header_fields(fp, hentry);
620                 fclose(fp);
621
622 #define SET_FILTER_KEY(hstr, idx)       \
623 {                                       \
624         *header = g_strdup(hstr);       \
625         *key = hentry[idx].body;        \
626         hentry[idx].body = NULL;        \
627 }
628
629                 if (hentry[H_X_BEENTHERE].body != NULL) {
630                         SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
631                 } else if (hentry[H_X_ML_NAME].body != NULL) {
632                         SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
633                 } else if (hentry[H_X_LIST].body != NULL) {
634                         SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
635                 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
636                         SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
637                 } else if (hentry[H_LIST_ID].body != NULL) {
638                         SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
639                         extract_list_id_str(*key);
640                 } else if (hentry[H_X_SEQUENCE].body != NULL) {
641                         gchar *p;
642
643                         SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
644                         p = *key;
645                         while (*p != '\0') {
646                                 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
647                                 while (g_ascii_isspace(*p)) p++;
648                                 if (g_ascii_isdigit(*p)) {
649                                         *p = '\0';
650                                         break;
651                                 }
652                         }
653                         g_strstrip(*key);
654                 } else if (hentry[H_SENDER].body != NULL) {
655                         SET_FILTER_KEY("header \"Sender\"", H_SENDER);
656                 } else if (hentry[H_LIST_POST].body != NULL) {
657                         SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
658                 } else if (msginfo->to) {
659                         *header = g_strdup("to");
660                         *key = g_strdup(msginfo->to);
661                 } else if (msginfo->subject) {
662                         *header = g_strdup("subject");
663                         *key = g_strdup(msginfo->subject);
664                 }
665
666 #undef SET_FILTER_KEY
667
668                 g_free(hentry[H_X_BEENTHERE].body);
669                 hentry[H_X_BEENTHERE].body = NULL;
670                 g_free(hentry[H_X_ML_NAME].body);
671                 hentry[H_X_ML_NAME].body = NULL;
672                 g_free(hentry[H_X_LIST].body);
673                 hentry[H_X_LIST].body = NULL;
674                 g_free(hentry[H_X_MAILING_LIST].body);
675                 hentry[H_X_MAILING_LIST].body = NULL;
676                 g_free(hentry[H_LIST_ID].body);
677                 hentry[H_LIST_ID].body = NULL;
678                 g_free(hentry[H_SENDER].body);
679                 hentry[H_SENDER].body = NULL;
680                 g_free(hentry[H_LIST_POST].body);
681                 hentry[H_LIST_POST].body = NULL;
682
683                 break;
684         case FILTER_BY_FROM:
685                 *header = g_strdup("from");
686                 *key = g_strdup(msginfo->from);
687                 break;
688         case FILTER_BY_TO:
689                 *header = g_strdup("to");
690                 *key = g_strdup(msginfo->to);
691                 break;
692         case FILTER_BY_SUBJECT:
693                 *header = g_strdup("subject");
694                 *key = g_strdup(msginfo->subject);
695                 break;
696         default:
697                 break;
698         }
699 }
700
701 void procmsg_empty_trash(FolderItem *trash)
702 {
703         GNode *node, *next;
704
705         if (!trash || 
706             (trash->stype != F_TRASH && 
707              !folder_has_parent_of_type(trash, F_TRASH)))
708                 return;
709
710         if (trash && trash->total_msgs > 0) {
711                 GSList *mlist = folder_item_get_msg_list(trash);
712                 GSList *cur;
713                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
714                         MsgInfo * msginfo = (MsgInfo *) cur->data;
715                         if (MSG_IS_LOCKED(msginfo->flags))
716                                 continue;
717                         if (msginfo->total_size != 0 && 
718                             msginfo->size != (off_t)msginfo->total_size)
719                                 partial_mark_for_delete(msginfo);
720
721                         procmsg_msginfo_free(msginfo);
722                 }
723                 g_slist_free(mlist);
724                 folder_item_remove_all_msg(trash);
725         }
726
727         if (!trash->node || !trash->node->children)
728                 return;
729
730         node = trash->node->children;
731         while (node != NULL) {
732                 next = node->next;
733                 procmsg_empty_trash(FOLDER_ITEM(node->data));
734                 node = next;
735         }
736 }
737
738 void procmsg_empty_all_trash(void)
739 {
740         FolderItem *trash;
741         GList *cur;
742
743         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
744                 Folder *folder = FOLDER(cur->data);
745                 trash = folder->trash;
746                 procmsg_empty_trash(trash);
747                 if (folder->account && folder->account->set_trash_folder && 
748                     folder_find_item_from_identifier(folder->account->trash_folder))
749                         procmsg_empty_trash(
750                                 folder_find_item_from_identifier(folder->account->trash_folder));
751         }
752 }
753
754 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
755 {
756         PrefsAccount *mailac = NULL;
757         FILE *fp;
758         int hnum;
759         gchar buf[BUFFSIZE];
760         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
761                                        {"SSV:",  NULL, FALSE},
762                                        {"R:",    NULL, FALSE},
763                                        {"NG:",   NULL, FALSE},
764                                        {"MAID:", NULL, FALSE},
765                                        {"NAID:", NULL, FALSE},
766                                        {"SCF:",  NULL, FALSE},
767                                        {"RMID:", NULL, FALSE},
768                                        {"FMID:", NULL, FALSE},
769                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
770                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
771                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
772                                        {NULL,    NULL, FALSE}};
773         
774         g_return_val_if_fail(file != NULL, NULL);
775
776         if ((fp = g_fopen(file, "rb")) == NULL) {
777                 FILE_OP_ERROR(file, "fopen");
778                 return NULL;
779         }
780
781         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
782                != -1) {
783                 gchar *p = buf + strlen(qentry[hnum].name);
784
785                 if (hnum == Q_MAIL_ACCOUNT_ID) {
786                         mailac = account_find_from_id(atoi(p));
787                         break;
788                 }
789         }
790         fclose(fp);
791         return mailac;
792 }
793
794 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
795 {
796         GSList *result = NULL;
797         GSList *orig = NULL;
798         PrefsAccount *last_account = NULL;
799         MsgInfo *msg = NULL;
800         GSList *cur = NULL;
801         gboolean nothing_to_sort = TRUE;
802
803         if (!list)
804                 return NULL;
805
806         orig = g_slist_copy(list);
807         
808         msg = (MsgInfo *)orig->data;
809         
810         for (cur = orig; cur; cur = cur->next)
811                 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
812         
813         debug_print("\n");
814
815 parse_again:    
816         nothing_to_sort = TRUE;
817         cur = orig;
818         while (cur) {
819                 gchar *file = NULL;
820                 PrefsAccount *ac = NULL;
821                 msg = (MsgInfo *)cur->data;
822                 file = folder_item_fetch_msg(queue, msg->msgnum);
823                 ac = procmsg_get_account_from_file(file);
824                 g_free(file);
825
826                 if (last_account == NULL || (ac != NULL && ac == last_account)) {
827                         result = g_slist_append(result, msg);
828                         orig = g_slist_remove(orig, msg);
829                         last_account = ac;
830                         nothing_to_sort = FALSE;
831                         goto parse_again;
832                 }
833                 cur = cur->next;
834         }
835         
836         if (orig || g_slist_length(orig)) {
837                 if (!last_account && nothing_to_sort) {
838                         /* can't find an account for the rest of the list */
839                         cur = orig;
840                         while (cur) {
841                                 result = g_slist_append(result, cur->data);
842                                 cur = cur->next;
843                         }
844                 } else {
845                         last_account = NULL;
846                         goto parse_again;
847                 }
848         }
849         
850         g_slist_free(orig);
851         
852         for (cur = result; cur; cur = cur->next)
853                 debug_print("sort after  %s\n", ((MsgInfo *)cur->data)->from);
854
855         debug_print("\n");
856
857         return result;
858 }
859
860 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
861 {
862         gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
863         PrefsAccount *ac = procmsg_get_account_from_file(file);
864         GSList *cur = elem;
865         g_free(file);
866         for (cur = elem; cur; cur = cur->next) {
867                 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
868                 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
869                 
870                 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
871                         if (procmsg_get_account_from_file(file) == ac) {
872                                 g_free(file);
873                                 return FALSE;
874                         }
875                 }
876                 
877                 g_free(file);
878         }
879         return TRUE;
880 }
881
882 /*!
883  *\brief        Send messages in queue
884  *
885  *\param        queue Queue folder to process
886  *\param        save_msgs Unused
887  *
888  *\return       Number of messages sent, negative if an error occurred
889  *              positive if no error occurred
890  */
891 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
892 {
893         gint sent = 0, err = 0;
894         GSList *list, *elem;
895         GSList *sorted_list = NULL;
896         GNode *node, *next;
897         static gboolean send_queue_lock = FALSE;
898         
899         if (send_queue_lock) {
900                 log_error(_("Already trying to send\n"));
901                 if (errstr) {
902                         if (*errstr) g_free(*errstr);
903                         *errstr = g_strdup_printf(_("Already trying to send."));
904                 }
905                 return -1;
906         }
907         send_queue_lock = TRUE;
908         if (!queue)
909                 queue = folder_get_default_queue();
910         g_return_val_if_fail(queue != NULL, -1);
911
912         folder_item_scan(queue);
913         list = folder_item_get_msg_list(queue);
914
915         /* sort the list per sender account; this helps reusing the same SMTP server */
916         sorted_list = procmsg_list_sort_by_account(queue, list);
917         
918         for (elem = sorted_list; elem != NULL; elem = elem->next) {
919                 gchar *file;
920                 MsgInfo *msginfo;
921                         
922                 msginfo = (MsgInfo *)(elem->data);
923                 if (!MSG_IS_LOCKED(msginfo->flags)) {
924                         file = folder_item_fetch_msg(queue, msginfo->msgnum);
925                         if (file) {
926                                 if (procmsg_send_message_queue_full(file, 
927                                                 !procmsg_is_last_for_account(queue, msginfo, elem),
928                                                 errstr) < 0) {
929                                         g_warning("Sending queued message %d failed.\n", 
930                                                   msginfo->msgnum);
931                                         err++;
932                                 } else {
933                                         sent++; 
934                                         folder_item_remove_msg(queue, msginfo->msgnum);
935                                 }
936                                 g_free(file);
937                         }
938                 }
939                 /* FIXME: supposedly if only one message is locked, and queue
940                  * is being flushed, the following free says something like 
941                  * "freeing msg ## in folder (nil)". */
942                 procmsg_msginfo_free(msginfo);
943         }
944
945         g_slist_free(sorted_list);
946         folder_item_scan(queue);
947
948         if (queue->node && queue->node->children) {
949                 node = queue->node->children;
950                 while (node != NULL) {
951                         int res = 0;
952                         next = node->next;
953                         res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
954                         if (res < 0) 
955                                 err = -res;
956                         else
957                                 sent += res;
958                         node = next;
959                 }
960         }
961         send_queue_lock = FALSE;
962         return (err != 0 ? -err : sent);
963 }
964
965 /*!
966  *\brief        Determine if a queue folder is empty
967  *
968  *\param        queue Queue folder to process
969  *
970  *\return       TRUE if the queue folder is empty, otherwise return FALSE
971  */
972 gboolean procmsg_queue_is_empty(FolderItem *queue)
973 {
974         GSList *list;
975         gboolean res = FALSE;
976         if (!queue)
977                 queue = folder_get_default_queue();
978         g_return_val_if_fail(queue != NULL, TRUE);
979
980         folder_item_scan(queue);
981         list = folder_item_get_msg_list(queue);
982         res = (list == NULL);
983         procmsg_msg_list_free(list);
984
985         if (res == TRUE) {
986                 GNode *node, *next;
987                 if (queue->node && queue->node->children) {
988                         node = queue->node->children;
989                         while (node != NULL) {
990                                 next = node->next;
991                                 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
992                                         return FALSE;
993                                 node = next;
994                         }
995                 }
996         }
997         return res;
998 }
999
1000 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1001 {
1002         FILE *fp, *outfp;
1003         gchar buf[BUFFSIZE];
1004         
1005         if ((fp = g_fopen(in, "rb")) == NULL) {
1006                 FILE_OP_ERROR(in, "fopen");
1007                 return -1;
1008         }
1009         if ((outfp = g_fopen(out, "wb")) == NULL) {
1010                 FILE_OP_ERROR(out, "fopen");
1011                 fclose(fp);
1012                 return -1;
1013         }
1014         while (fgets(buf, sizeof(buf), fp) != NULL) {
1015                 /* new way */
1016                 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1017                         strlen("X-Sylpheed-End-Special-Headers:")))
1018                         break;
1019                 /* old way */
1020                 if (buf[0] == '\r' || buf[0] == '\n') break;
1021                 /* from other mailers */
1022                 if (!strncmp(buf, "Date: ", 6)
1023                 ||  !strncmp(buf, "To: ", 4)
1024                 ||  !strncmp(buf, "From: ", 6)
1025                 ||  !strncmp(buf, "Subject: ", 9)) {
1026                         rewind(fp);
1027                         break;
1028                 }
1029         }
1030         while (fgets(buf, sizeof(buf), fp) != NULL)
1031                 fputs(buf, outfp);
1032         fclose(outfp);
1033         fclose(fp);
1034         return 0;
1035 }
1036
1037 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1038                             gboolean is_queued)
1039 {
1040         gint num;
1041         MsgInfo *msginfo, *tmp_msginfo;
1042         MsgFlags flag = {0, 0};
1043
1044         debug_print("saving sent message...\n");
1045
1046         if (!outbox)
1047                 outbox = folder_get_default_outbox();
1048         g_return_val_if_fail(outbox != NULL, -1);
1049
1050         /* remove queueing headers */
1051         if (is_queued) {
1052                 gchar tmp[MAXPATHLEN + 1];
1053
1054                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1055                            get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1056                 
1057                 if (procmsg_remove_special_headers(file, tmp) !=0)
1058                         return -1;
1059
1060                 folder_item_scan(outbox);
1061                 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1062                         g_warning("can't save message\n");
1063                         g_unlink(tmp);
1064                         return -1;
1065                 }
1066         } else {
1067                 folder_item_scan(outbox);
1068                 if ((num = folder_item_add_msg
1069                         (outbox, file, &flag, FALSE)) < 0) {
1070                         g_warning("can't save message\n");
1071                         return -1;
1072                 }
1073                 return 0;
1074         }
1075         msginfo = folder_item_get_msginfo(outbox, num);         /* refcnt++ */
1076         tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);   /* refcnt++ */ 
1077         if (msginfo != NULL) {
1078                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1079                 procmsg_msginfo_free(msginfo);                  /* refcnt-- */
1080                 /* tmp_msginfo == msginfo */
1081                 if (tmp_msginfo && (msginfo->dispositionnotificationto || 
1082                     msginfo->returnreceiptto)) {
1083                         procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 
1084                         procmsg_msginfo_free(msginfo);          /* refcnt-- */
1085                 }       
1086         }
1087
1088         return 0;
1089 }
1090
1091 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1092 {
1093         static const gchar *def_cmd = "lpr %s";
1094         static guint id = 0;
1095         gchar *prtmp;
1096         FILE *tmpfp, *prfp;
1097         gchar buf[1024];
1098         gchar *p;
1099
1100         g_return_if_fail(msginfo);
1101
1102         if (procmime_msginfo_is_encrypted(msginfo))
1103                 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1104         else
1105                 tmpfp = procmime_get_first_text_content(msginfo);
1106         if (tmpfp == NULL) {
1107                 g_warning("Can't get text part\n");
1108                 return;
1109         }
1110
1111         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1112                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1113
1114         if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1115                 FILE_OP_ERROR(prtmp, "fopen");
1116                 g_free(prtmp);
1117                 fclose(tmpfp);
1118                 return;
1119         }
1120
1121         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1122         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1123         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
1124         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
1125         if (msginfo->newsgroups)
1126                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1127         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1128         fputc('\n', prfp);
1129
1130         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1131                 fputs(buf, prfp);
1132
1133         fclose(prfp);
1134         fclose(tmpfp);
1135
1136         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1137             !strchr(p + 2, '%'))
1138                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1139         else {
1140                 if (cmdline)
1141                         g_warning("Print command line is invalid: '%s'\n",
1142                                   cmdline);
1143                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1144         }
1145
1146         g_free(prtmp);
1147
1148         g_strchomp(buf);
1149         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1150         system(buf);
1151 }
1152
1153 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1154 {
1155         msginfo->refcnt++;
1156         
1157         return msginfo;
1158 }
1159
1160 MsgInfo *procmsg_msginfo_new(void)
1161 {
1162         MsgInfo *newmsginfo;
1163
1164         newmsginfo = g_new0(MsgInfo, 1);
1165         newmsginfo->refcnt = 1;
1166         
1167         return newmsginfo;
1168 }
1169
1170 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1171 {
1172         MsgInfo *newmsginfo;
1173         GSList *refs;
1174
1175         if (msginfo == NULL) return NULL;
1176
1177         newmsginfo = g_new0(MsgInfo, 1);
1178
1179         newmsginfo->refcnt = 1;
1180
1181 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1182 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1183                         g_strdup(msginfo->mmb) : NULL
1184
1185         MEMBCOPY(msgnum);
1186         MEMBCOPY(size);
1187         MEMBCOPY(mtime);
1188         MEMBCOPY(date_t);
1189
1190         MEMBCOPY(flags);
1191
1192         MEMBDUP(fromname);
1193
1194         MEMBDUP(date);
1195         MEMBDUP(from);
1196         MEMBDUP(to);
1197         MEMBDUP(cc);
1198         MEMBDUP(newsgroups);
1199         MEMBDUP(subject);
1200         MEMBDUP(msgid);
1201         MEMBDUP(inreplyto);
1202         MEMBDUP(xref);
1203
1204         MEMBCOPY(folder);
1205         MEMBCOPY(to_folder);
1206
1207         MEMBDUP(face);
1208         MEMBDUP(xface);
1209         MEMBDUP(dispositionnotificationto);
1210         MEMBDUP(returnreceiptto);
1211
1212         refs = msginfo->references;
1213         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1214                 newmsginfo->references = g_slist_prepend
1215                         (newmsginfo->references, g_strdup(refs->data)); 
1216         }
1217         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1218
1219         MEMBCOPY(score);
1220         MEMBDUP(plaintext_file);
1221
1222         return newmsginfo;
1223 }
1224
1225 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1226 {
1227         MsgInfo *full_msginfo;
1228         gchar *file;
1229
1230         if (msginfo == NULL) return NULL;
1231
1232         file = procmsg_get_message_file_path(msginfo);
1233         if (!file || !is_file_exist(file)) {
1234                 g_free(file);
1235                 file = procmsg_get_message_file(msginfo);
1236         }
1237         if (!file || !is_file_exist(file)) {
1238                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1239                 return NULL;
1240         }
1241
1242         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1243         g_free(file);
1244         if (!full_msginfo) return NULL;
1245
1246         /* CLAWS: make sure we add the missing members; see: 
1247          * procheader.c::procheader_get_headernames() */
1248         if (!msginfo->list_post)
1249                 msginfo->list_post = g_strdup(full_msginfo->list_post);
1250         if (!msginfo->list_subscribe)
1251                 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1252         if (!msginfo->list_unsubscribe)
1253                 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1254         if (!msginfo->list_help)
1255                 msginfo->list_help = g_strdup(full_msginfo->list_help);
1256         if (!msginfo->list_archive)
1257                 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1258         if (!msginfo->list_owner)
1259                 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1260         if (!msginfo->xface)
1261                 msginfo->xface = g_strdup(full_msginfo->xface);
1262         if (!msginfo->face)
1263                 msginfo->face = g_strdup(full_msginfo->face);
1264         if (!msginfo->dispositionnotificationto)
1265                 msginfo->dispositionnotificationto = 
1266                         g_strdup(full_msginfo->dispositionnotificationto);
1267         if (!msginfo->returnreceiptto)
1268                 msginfo->returnreceiptto = g_strdup
1269                         (full_msginfo->returnreceiptto);
1270         if (!msginfo->partial_recv && full_msginfo->partial_recv)
1271                 msginfo->partial_recv = g_strdup
1272                         (full_msginfo->partial_recv);
1273         msginfo->total_size = full_msginfo->total_size;
1274         if (!msginfo->account_server && full_msginfo->account_server)
1275                 msginfo->account_server = g_strdup
1276                         (full_msginfo->account_server);
1277         if (!msginfo->account_login && full_msginfo->account_login)
1278                 msginfo->account_login = g_strdup
1279                         (full_msginfo->account_login);
1280         msginfo->planned_download = full_msginfo->planned_download;
1281         procmsg_msginfo_free(full_msginfo);
1282
1283         return procmsg_msginfo_new_ref(msginfo);
1284 }
1285
1286 void procmsg_msginfo_free(MsgInfo *msginfo)
1287 {
1288         if (msginfo == NULL) return;
1289
1290         msginfo->refcnt--;
1291         if (msginfo->refcnt > 0)
1292                 return;
1293
1294         if (msginfo->to_folder) {
1295                 msginfo->to_folder->op_count--;
1296                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1297         }
1298
1299         g_free(msginfo->fromspace);
1300         g_free(msginfo->returnreceiptto);
1301         g_free(msginfo->dispositionnotificationto);
1302         g_free(msginfo->xface);
1303         g_free(msginfo->face);
1304
1305         g_free(msginfo->fromname);
1306
1307         g_free(msginfo->date);
1308         g_free(msginfo->from);
1309         g_free(msginfo->to);
1310         g_free(msginfo->cc);
1311         g_free(msginfo->newsgroups);
1312         g_free(msginfo->subject);
1313         g_free(msginfo->msgid);
1314         g_free(msginfo->inreplyto);
1315         g_free(msginfo->xref);
1316
1317         g_free(msginfo->list_post);
1318         g_free(msginfo->list_subscribe);
1319         g_free(msginfo->list_unsubscribe);
1320         g_free(msginfo->list_help);
1321         g_free(msginfo->list_archive);
1322         g_free(msginfo->list_owner);
1323
1324         g_free(msginfo->partial_recv);
1325         g_free(msginfo->account_server);
1326         g_free(msginfo->account_login);
1327         
1328         slist_free_strings(msginfo->references);
1329         g_slist_free(msginfo->references);
1330
1331         g_free(msginfo->plaintext_file);
1332
1333         g_free(msginfo);
1334 }
1335
1336 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1337 {
1338         guint memusage = 0;
1339         GSList *refs;
1340         
1341         memusage += sizeof(MsgInfo);
1342         if (msginfo->fromname)
1343                 memusage += strlen(msginfo->fromname);
1344         if (msginfo->date)
1345                 memusage += strlen(msginfo->date);
1346         if (msginfo->from)
1347                 memusage += strlen(msginfo->from);
1348         if (msginfo->to)
1349                 memusage += strlen(msginfo->to);
1350         if (msginfo->cc)
1351                 memusage += strlen(msginfo->cc);
1352         if (msginfo->newsgroups)
1353                 memusage += strlen(msginfo->newsgroups);
1354         if (msginfo->subject)
1355                 memusage += strlen(msginfo->subject);
1356         if (msginfo->msgid)
1357                 memusage += strlen(msginfo->msgid);
1358         if (msginfo->inreplyto)
1359                 memusage += strlen(msginfo->inreplyto);
1360         if (msginfo->xface)
1361                 memusage += strlen(msginfo->xface);
1362         if (msginfo->face)
1363                 memusage += strlen(msginfo->face);
1364         if (msginfo->dispositionnotificationto)
1365                 memusage += strlen(msginfo->dispositionnotificationto);
1366         if (msginfo->returnreceiptto)
1367                 memusage += strlen(msginfo->returnreceiptto);
1368         for (refs = msginfo->references; refs; refs=refs->next) {
1369                 gchar *r = (gchar *)refs->data;
1370                 memusage += r?strlen(r):0;
1371         }
1372         if (msginfo->fromspace)
1373                 memusage += strlen(msginfo->fromspace);
1374
1375         return memusage;
1376 }
1377
1378 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1379 {
1380         const MsgInfo *msginfo1 = a;
1381         const MsgInfo *msginfo2 = b;
1382
1383         if (!msginfo1)
1384                 return -1;
1385         if (!msginfo2)
1386                 return -1;
1387
1388         return msginfo1->msgnum - msginfo2->msgnum;
1389 }
1390
1391 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1392 {
1393         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1394                                        {"SSV:",  NULL, FALSE},
1395                                        {"R:",    NULL, FALSE},
1396                                        {"NG:",   NULL, FALSE},
1397                                        {"MAID:", NULL, FALSE},
1398                                        {"NAID:", NULL, FALSE},
1399                                        {"SCF:",  NULL, FALSE},
1400                                        {"RMID:", NULL, FALSE},
1401                                        {"FMID:", NULL, FALSE},
1402                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1403                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1404                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1405                                        {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1406                                        {NULL,    NULL, FALSE}};
1407         FILE *fp;
1408         gint filepos;
1409         gint mailval = 0, newsval = 0;
1410         gchar *from = NULL;
1411         gchar *smtpserver = NULL;
1412         GSList *to_list = NULL;
1413         GSList *newsgroup_list = NULL;
1414         gchar *savecopyfolder = NULL;
1415         gchar *replymessageid = NULL;
1416         gchar *fwdmessageid = NULL;
1417         gchar *privacy_system = NULL;
1418         gboolean encrypt = FALSE;
1419         gchar *encrypt_data = NULL;
1420         gchar buf[BUFFSIZE];
1421         gint hnum;
1422         PrefsAccount *mailac = NULL, *newsac = NULL;
1423         gboolean save_clear_text = TRUE;
1424         gchar *tmp_enc_file = NULL;
1425
1426         int local = 0;
1427
1428         g_return_val_if_fail(file != NULL, -1);
1429
1430         if ((fp = g_fopen(file, "rb")) == NULL) {
1431                 FILE_OP_ERROR(file, "fopen");
1432                 if (errstr) {
1433                         if (*errstr) g_free(*errstr);
1434                         *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1435                 }
1436                 return -1;
1437         }
1438
1439         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1440                != -1) {
1441                 gchar *p = buf + strlen(qentry[hnum].name);
1442
1443                 switch (hnum) {
1444                 case Q_SENDER:
1445                         if (from == NULL) 
1446                                 from = g_strdup(p);
1447                         break;
1448                 case Q_SMTPSERVER:
1449                         if (smtpserver == NULL) 
1450                                 smtpserver = g_strdup(p);
1451                         break;
1452                 case Q_RECIPIENTS:
1453                         to_list = address_list_append(to_list, p);
1454                         break;
1455                 case Q_NEWSGROUPS:
1456                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1457                         break;
1458                 case Q_MAIL_ACCOUNT_ID:
1459                         mailac = account_find_from_id(atoi(p));
1460                         break;
1461                 case Q_NEWS_ACCOUNT_ID:
1462                         newsac = account_find_from_id(atoi(p));
1463                         break;
1464                 case Q_SAVE_COPY_FOLDER:
1465                         if (savecopyfolder == NULL) 
1466                                 savecopyfolder = g_strdup(p);
1467                         break;
1468                 case Q_REPLY_MESSAGE_ID:
1469                         if (replymessageid == NULL) 
1470                                 replymessageid = g_strdup(p);
1471                         break;
1472                 case Q_FWD_MESSAGE_ID:
1473                         if (fwdmessageid == NULL) 
1474                                 fwdmessageid = g_strdup(p);
1475                         break;
1476                 case Q_PRIVACY_SYSTEM:
1477                         if (privacy_system == NULL) 
1478                                 privacy_system = g_strdup(p);
1479                         break;
1480                 case Q_ENCRYPT:
1481                         if (p[0] == '1') 
1482                                 encrypt = TRUE;
1483                         break;
1484                 case Q_ENCRYPT_DATA:
1485                         if (encrypt_data == NULL) 
1486                                 encrypt_data = g_strdup(p);
1487                         break;
1488                 case Q_SYLPHEED_HDRS:
1489                         /* end of special headers reached */
1490                         goto send_mail; /* can't "break;break;" */
1491                 }
1492         }
1493 send_mail:
1494         filepos = ftell(fp);
1495
1496         if (encrypt) {
1497                 MimeInfo *mimeinfo;
1498
1499                 if (mailac && mailac->save_encrypted_as_clear_text 
1500                 &&  !mailac->encrypt_to_self)
1501                         save_clear_text = TRUE;
1502                 else
1503                         save_clear_text = FALSE;
1504
1505                 fclose(fp);
1506                 fp = NULL;
1507
1508                 mimeinfo = procmime_scan_queue_file(file);
1509                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1510                 || (fp = my_tmpfile()) == NULL
1511                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1512                         if (fp)
1513                                 fclose(fp);
1514                         procmime_mimeinfo_free_all(mimeinfo);
1515                         g_free(from);
1516                         g_free(smtpserver);
1517                         slist_free_strings(to_list);
1518                         g_slist_free(to_list);
1519                         slist_free_strings(newsgroup_list);
1520                         g_slist_free(newsgroup_list);
1521                         g_free(savecopyfolder);
1522                         g_free(replymessageid);
1523                         g_free(fwdmessageid);
1524                         g_free(privacy_system);
1525                         g_free(encrypt_data);
1526                         if (errstr) {
1527                                 if (*errstr) g_free(*errstr);
1528                                 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1529                                                 privacy_get_error());
1530                         }
1531                         return -1;
1532                 }
1533                 
1534                 rewind(fp);
1535                 if (!save_clear_text) {
1536                         gchar *content = NULL;
1537                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1538                         if (tmpfp) {
1539                                 fclose(tmpfp);
1540
1541                                 content = file_read_stream_to_str(fp);
1542                                 rewind(fp);
1543
1544                                 str_write_to_file(content, tmp_enc_file);
1545                                 g_free(content);
1546                         } else {
1547                                 g_warning("couldn't get tempfile\n");
1548                         }
1549                 } 
1550                 
1551                 procmime_mimeinfo_free_all(mimeinfo);
1552                 
1553                 filepos = 0;
1554         }
1555
1556         if (to_list) {
1557                 debug_print("Sending message by mail\n");
1558                 if (!from) {
1559                         if (errstr) {
1560                                 if (*errstr) g_free(*errstr);
1561                                 *errstr = g_strdup_printf(_("Queued message header is broken."));
1562                         }
1563                         mailval = -1;
1564                 } else if (mailac && mailac->use_mail_command &&
1565                            mailac->mail_command && (* mailac->mail_command)) {
1566                         mailval = send_message_local(mailac->mail_command, fp);
1567                         local = 1;
1568                 } else {
1569                         if (!mailac) {
1570                                 mailac = account_find_from_smtp_server(from, smtpserver);
1571                                 if (!mailac) {
1572                                         g_warning("Account not found. "
1573                                                     "Using current account...\n");
1574                                         mailac = cur_account;
1575                                 }
1576                         }
1577
1578                         if (mailac) {
1579                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1580                                 if (mailval == -1 && errstr) {
1581                                         if (*errstr) g_free(*errstr);
1582                                         *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1583                                 }
1584                         } else {
1585                                 PrefsAccount tmp_ac;
1586
1587                                 g_warning("Account not found.\n");
1588
1589                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1590                                 tmp_ac.address = from;
1591                                 tmp_ac.smtp_server = smtpserver;
1592                                 tmp_ac.smtpport = SMTP_PORT;
1593                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1594                                 if (mailval == -1 && errstr) {
1595                                         if (*errstr) g_free(*errstr);
1596                                         *errstr = g_strdup_printf(_("No specific account has been found to "
1597                                                         "send, and an error happened during SMTP session."));
1598                                 }
1599                         }
1600                 }
1601         } else if (!to_list && !newsgroup_list) {
1602                 if (errstr) {
1603                         if (*errstr) g_free(*errstr);
1604                         *errstr = g_strdup(_("Couldn't determine sending informations. "
1605                                 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1606                 }
1607                 mailval = -1;
1608         }
1609
1610         fseek(fp, filepos, SEEK_SET);
1611         if (newsgroup_list && (mailval == 0)) {
1612                 Folder *folder;
1613                 gchar *tmp = NULL;
1614                 FILE *tmpfp;
1615
1616                 /* write to temporary file */
1617                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1618                             G_DIR_SEPARATOR, (gint)file);
1619                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1620                         FILE_OP_ERROR(tmp, "fopen");
1621                         newsval = -1;
1622                         alertpanel_error(_("Could not create temporary file for news sending."));
1623                 } else {
1624                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1625                                 FILE_OP_ERROR(tmp, "chmod");
1626                                 g_warning("can't change file mode\n");
1627                         }
1628
1629                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1630                                 if (fputs(buf, tmpfp) == EOF) {
1631                                         FILE_OP_ERROR(tmp, "fputs");
1632                                         newsval = -1;
1633                                         if (errstr) {
1634                                                 if (*errstr) g_free(*errstr);
1635                                                 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1636                                         }
1637                                 }
1638                         }
1639                         fclose(tmpfp);
1640
1641                         if (newsval == 0) {
1642                                 debug_print("Sending message by news\n");
1643
1644                                 folder = FOLDER(newsac->folder);
1645
1646                                 newsval = news_post(folder, tmp);
1647                                 if (newsval < 0 && errstr)  {
1648                                         if (*errstr) g_free(*errstr);
1649                                         *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1650                                          newsac->nntp_server);
1651                                 }
1652                         }
1653                         g_unlink(tmp);
1654                 }
1655                 g_free(tmp);
1656         }
1657
1658         fclose(fp);
1659
1660         /* save message to outbox */
1661         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1662                 FolderItem *outbox;
1663
1664                 debug_print("saving sent message...\n");
1665
1666                 outbox = folder_find_item_from_identifier(savecopyfolder);
1667                 if (!outbox)
1668                         outbox = folder_get_default_outbox();
1669                         
1670                 if (save_clear_text || tmp_enc_file == NULL) {
1671                         procmsg_save_to_outbox(outbox, file, TRUE);
1672                 } else {
1673                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1674                 }
1675         }
1676
1677         if (tmp_enc_file != NULL) {
1678                 g_unlink(tmp_enc_file);
1679                 free(tmp_enc_file);
1680                 tmp_enc_file = NULL;
1681         }
1682
1683         if (replymessageid != NULL || fwdmessageid != NULL) {
1684                 gchar **tokens;
1685                 FolderItem *item;
1686                 
1687                 if (replymessageid != NULL)
1688                         tokens = g_strsplit(replymessageid, "\t", 0);
1689                 else
1690                         tokens = g_strsplit(fwdmessageid, "\t", 0);
1691                 item = folder_find_item_from_identifier(tokens[0]);
1692
1693                 /* check if queued message has valid folder and message id */
1694                 if (item != NULL && tokens[2] != NULL) {
1695                         MsgInfo *msginfo;
1696                         
1697                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1698                 
1699                         /* check if referring message exists and has a message id */
1700                         if ((msginfo != NULL) && 
1701                             (msginfo->msgid != NULL) &&
1702                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1703                                 procmsg_msginfo_free(msginfo);
1704                                 msginfo = NULL;
1705                         }
1706                         
1707                         if (msginfo == NULL) {
1708                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1709                         }
1710                         
1711                         if (msginfo != NULL) {
1712                                 if (replymessageid != NULL) {
1713                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1714                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1715                                 }  else {
1716                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1717                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1718                                 }
1719                                 procmsg_msginfo_free(msginfo);
1720                         }
1721                 }
1722                 g_strfreev(tokens);
1723         }
1724
1725         g_free(from);
1726         g_free(smtpserver);
1727         slist_free_strings(to_list);
1728         g_slist_free(to_list);
1729         slist_free_strings(newsgroup_list);
1730         g_slist_free(newsgroup_list);
1731         g_free(savecopyfolder);
1732         g_free(replymessageid);
1733         g_free(fwdmessageid);
1734         g_free(privacy_system);
1735         g_free(encrypt_data);
1736
1737         return (newsval != 0 ? newsval : mailval);
1738 }
1739
1740 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1741 {
1742         return procmsg_send_message_queue_full(file, FALSE, errstr);
1743 }
1744
1745 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1746 {
1747         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1748
1749         /* NEW flag */
1750         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1751                 item->new_msgs++;
1752         }
1753
1754         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1755                 item->new_msgs--;
1756         }
1757
1758         /* UNREAD flag */
1759         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1760                 item->unread_msgs++;
1761                 if (procmsg_msg_has_marked_parent(msginfo))
1762                         item->unreadmarked_msgs++;
1763         }
1764
1765         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1766                 item->unread_msgs--;
1767                 if (procmsg_msg_has_marked_parent(msginfo))
1768                         item->unreadmarked_msgs--;
1769         }
1770         
1771         /* MARK flag */
1772         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1773                 procmsg_update_unread_children(msginfo, TRUE);
1774                 item->marked_msgs++;
1775         }
1776
1777         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1778                 procmsg_update_unread_children(msginfo, FALSE);
1779                 item->marked_msgs--;
1780         }
1781 }
1782
1783 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1784 {
1785         FolderItem *item;
1786         MsgInfoUpdate msginfo_update;
1787         MsgPermFlags perm_flags_new, perm_flags_old;
1788         MsgTmpFlags tmp_flags_old;
1789
1790         g_return_if_fail(msginfo != NULL);
1791         item = msginfo->folder;
1792         g_return_if_fail(item != NULL);
1793         
1794         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1795
1796         /* Perm Flags handling */
1797         perm_flags_old = msginfo->flags.perm_flags;
1798         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1799         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1800                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1801         }
1802
1803         if (perm_flags_old != perm_flags_new) {
1804                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1805
1806                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1807
1808         }
1809
1810         /* Tmp flags handling */
1811         tmp_flags_old = msginfo->flags.tmp_flags;
1812         msginfo->flags.tmp_flags |= tmp_flags;
1813
1814         /* update notification */
1815         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1816                 msginfo_update.msginfo = msginfo;
1817                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1818                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1819                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1820         }
1821 }
1822
1823 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1824 {
1825         FolderItem *item;
1826         MsgInfoUpdate msginfo_update;
1827         MsgPermFlags perm_flags_new, perm_flags_old;
1828         MsgTmpFlags tmp_flags_old;
1829
1830         g_return_if_fail(msginfo != NULL);
1831         item = msginfo->folder;
1832         g_return_if_fail(item != NULL);
1833         
1834         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1835
1836         /* Perm Flags handling */
1837         perm_flags_old = msginfo->flags.perm_flags;
1838         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1839         
1840         if (perm_flags_old != perm_flags_new) {
1841                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1842
1843                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1844         }
1845
1846         /* Tmp flags hanlding */
1847         tmp_flags_old = msginfo->flags.tmp_flags;
1848         msginfo->flags.tmp_flags &= ~tmp_flags;
1849
1850         /* update notification */
1851         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1852                 msginfo_update.msginfo = msginfo;
1853                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1854                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1855                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1856         }
1857 }
1858
1859 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1860                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1861                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1862 {
1863         FolderItem *item;
1864         MsgInfoUpdate msginfo_update;
1865         MsgPermFlags perm_flags_new, perm_flags_old;
1866         MsgTmpFlags tmp_flags_old;
1867
1868         g_return_if_fail(msginfo != NULL);
1869         item = msginfo->folder;
1870         g_return_if_fail(item != NULL);
1871         
1872         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1873
1874         /* Perm Flags handling */
1875         perm_flags_old = msginfo->flags.perm_flags;
1876         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1877         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1878                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1879         }
1880
1881         if (perm_flags_old != perm_flags_new) {
1882                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1883
1884                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1885
1886         }
1887
1888         /* Tmp flags handling */
1889         tmp_flags_old = msginfo->flags.tmp_flags;
1890         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1891         msginfo->flags.tmp_flags |= add_tmp_flags;
1892
1893         /* update notification */
1894         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1895                 msginfo_update.msginfo = msginfo;
1896                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1897                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1898                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1899         }
1900 }
1901
1902 /*!
1903  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1904  *
1905  *\param        info Current message
1906  *\param        perm_flags Flags to be checked
1907  *\param        parentmsgs Hash of prior msgs to avoid loops
1908  *
1909  *\return       gboolean TRUE if perm_flags are found
1910  */
1911 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1912                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1913 {
1914         MsgInfo *tmp;
1915
1916         g_return_val_if_fail(info != NULL, FALSE);
1917
1918         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1919                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1920                                 info->inreplyto);
1921                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1922                         procmsg_msginfo_free(tmp);
1923                         return TRUE;
1924                 } else if (tmp != NULL) {
1925                         gboolean result;
1926
1927                         if (g_hash_table_lookup(parentmsgs, info)) {
1928                                 debug_print("loop detected: %d\n",
1929                                         info->msgnum);
1930                                 result = FALSE;
1931                         } else {
1932                                 g_hash_table_insert(parentmsgs, info, "1");
1933                                 result = procmsg_msg_has_flagged_parent_real(
1934                                     tmp, perm_flags, parentmsgs);
1935                         }
1936                         procmsg_msginfo_free(tmp);
1937                         return result;
1938                 } else {
1939                         return FALSE;
1940                 }
1941         } else
1942                 return FALSE;
1943 }
1944
1945 /*!
1946  *\brief        Callback for cleaning up hash of parentmsgs
1947  */
1948 gboolean parentmsgs_hash_remove(gpointer key,
1949                             gpointer value,
1950                             gpointer user_data)
1951 {
1952         return TRUE;
1953 }
1954
1955 /*!
1956  *\brief        Set up list of parentmsgs
1957  *              See procmsg_msg_has_flagged_parent_real()
1958  */
1959 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1960 {
1961         gboolean result;
1962         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1963
1964         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1965         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1966         g_hash_table_destroy(parentmsgs);
1967         return result;
1968 }
1969
1970 /*!
1971  *\brief        Check if msgs prior in thread are marked
1972  *              See procmsg_msg_has_flagged_parent_real()
1973  */
1974 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1975 {
1976         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1977 }
1978
1979
1980 GSList *procmsg_find_children_func(MsgInfo *info, 
1981                                    GSList *children, GSList *all)
1982 {
1983         GSList *cur;
1984
1985         g_return_val_if_fail(info!=NULL, children);
1986         if (info->msgid == NULL)
1987                 return children;
1988
1989         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1990                 MsgInfo *tmp = (MsgInfo *)cur->data;
1991                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1992                         /* Check if message is already in the list */
1993                         if ((children == NULL) || 
1994                             (g_slist_index(children, tmp) == -1)) {
1995                                 children = g_slist_prepend(children,
1996                                                 procmsg_msginfo_new_ref(tmp));
1997                                 children = procmsg_find_children_func(tmp, 
1998                                                         children, 
1999                                                         all);
2000                         }
2001                 }
2002         }
2003         return children;
2004 }
2005
2006 GSList *procmsg_find_children (MsgInfo *info)
2007 {
2008         GSList *children;
2009         GSList *all, *cur;
2010
2011         g_return_val_if_fail(info!=NULL, NULL);
2012         all = folder_item_get_msg_list(info->folder);
2013         children = procmsg_find_children_func(info, NULL, all);
2014         if (children != NULL) {
2015                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2016                         /* this will not free the used pointers
2017                            created with procmsg_msginfo_new_ref */
2018                         procmsg_msginfo_free((MsgInfo *)cur->data);
2019                 }
2020         }
2021         g_slist_free(all);
2022
2023         return children;
2024 }
2025
2026 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2027 {
2028         GSList *children = procmsg_find_children(info);
2029         GSList *cur;
2030         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2031                 MsgInfo *tmp = (MsgInfo *)cur->data;
2032                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2033                         if(newly_marked) 
2034                                 info->folder->unreadmarked_msgs++;
2035                         else
2036                                 info->folder->unreadmarked_msgs--;
2037                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2038                 }
2039                 procmsg_msginfo_free(tmp);
2040         }
2041         g_slist_free(children);
2042 }
2043
2044 /**
2045  * Set the destination folder for a copy or move operation
2046  *
2047  * \param msginfo The message which's destination folder is changed
2048  * \param to_folder The destination folder for the operation
2049  */
2050 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2051 {
2052         if(msginfo->to_folder != NULL) {
2053                 msginfo->to_folder->op_count--;
2054                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2055         }
2056         msginfo->to_folder = to_folder;
2057         if(to_folder != NULL) {
2058                 to_folder->op_count++;
2059                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2060         }
2061 }
2062
2063 /**
2064  * Apply filtering actions to the msginfo
2065  *
2066  * \param msginfo The MsgInfo describing the message that should be filtered
2067  * \return TRUE if the message was moved and MsgInfo is now invalid,
2068  *         FALSE otherwise
2069  */
2070 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2071 {
2072         MailFilteringData mail_filtering_data;
2073                         
2074         mail_filtering_data.msginfo = msginfo;                  
2075         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2076                 return TRUE;
2077         }
2078
2079         /* filter if enabled in prefs or move to inbox if not */
2080         if((filtering_rules != NULL) &&
2081            filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2082                 return TRUE;
2083         }
2084                 
2085         return FALSE;
2086 }
2087
2088 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2089 {
2090         MsgInfo *tmp_msginfo = NULL;
2091         MsgFlags flags = {0, 0};
2092         gchar *tmpfile = get_tmp_file();
2093         FILE *fp = g_fopen(tmpfile, "wb");
2094         
2095         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2096             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2097                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2098                 if (fp) 
2099                         fclose(fp);
2100                 g_free(tmpfile);
2101                 return NULL;
2102         }
2103         
2104         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2105                 fclose(fp);
2106                 fp = NULL;
2107                 tmp_msginfo = procheader_parse_file(
2108                         tmpfile, flags, 
2109                         TRUE, FALSE);
2110         }
2111         if (fp)
2112                 fclose(fp);
2113
2114         if (tmp_msginfo != NULL) {
2115                 if (src_msginfo)
2116                         tmp_msginfo->folder = src_msginfo->folder;
2117                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2118         } else {
2119                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2120         }
2121
2122         g_free(tmpfile);
2123
2124         return tmp_msginfo;
2125 }
2126
2127 static GSList *spam_learners = NULL;
2128
2129 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2130 {
2131         if (!g_slist_find(spam_learners, learn_func))
2132                 spam_learners = g_slist_append(spam_learners, learn_func);
2133         if (mainwindow_get_mainwindow()) {
2134                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2135                 summary_set_menu_sensitive(
2136                         mainwindow_get_mainwindow()->summaryview);
2137                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2138         }
2139 }
2140
2141 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2142 {
2143         spam_learners = g_slist_remove(spam_learners, learn_func);
2144         if (mainwindow_get_mainwindow()) {
2145                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2146                 summary_set_menu_sensitive(
2147                         mainwindow_get_mainwindow()->summaryview);
2148                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2149         }
2150 }
2151
2152 gboolean procmsg_spam_can_learn(void)
2153 {
2154         return g_slist_length(spam_learners) > 0;
2155 }
2156
2157 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2158 {
2159         GSList *cur = spam_learners;
2160         int ret = 0;
2161         for (; cur; cur = cur->next) {
2162                 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2163                 ret |= func(info, list, spam);
2164         }
2165         return ret;
2166 }
2167
2168 static gchar *spam_folder_item = NULL;
2169 void procmsg_spam_set_folder (const char *item_identifier)
2170 {
2171         g_free(spam_folder_item);
2172         if (item_identifier)
2173                 spam_folder_item = g_strdup(item_identifier);
2174         else
2175                 spam_folder_item = NULL;
2176 }
2177
2178 FolderItem *procmsg_spam_get_folder (void)
2179 {
2180         FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2181         return item ? item : folder_get_default_trash();
2182 }