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