2006-03-16 [colin] 2.0.0cvs141
[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                 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1436
1437                 fclose(fp);
1438                 fp = NULL;
1439
1440                 mimeinfo = procmime_scan_queue_file(file);
1441                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1442                 || (fp = my_tmpfile()) == NULL
1443                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1444                         if (fp)
1445                                 fclose(fp);
1446                         procmime_mimeinfo_free_all(mimeinfo);
1447                         g_free(from);
1448                         g_free(smtpserver);
1449                         slist_free_strings(to_list);
1450                         g_slist_free(to_list);
1451                         slist_free_strings(newsgroup_list);
1452                         g_slist_free(newsgroup_list);
1453                         g_free(savecopyfolder);
1454                         g_free(replymessageid);
1455                         g_free(fwdmessageid);
1456                         g_free(privacy_system);
1457                         g_free(encrypt_data);
1458                         return -1;
1459                 }
1460                 
1461                 rewind(fp);
1462                 if (!save_clear_text) {
1463                         gchar *content = NULL;
1464                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1465                         if (tmpfp) {
1466                                 fclose(tmpfp);
1467
1468                                 content = file_read_stream_to_str(fp);
1469                                 rewind(fp);
1470
1471                                 str_write_to_file(content, tmp_enc_file);
1472                                 g_free(content);
1473                         } else {
1474                                 g_warning("couldn't get tempfile\n");
1475                         }
1476                 } 
1477                 
1478                 procmime_mimeinfo_free_all(mimeinfo);
1479                 
1480                 filepos = 0;
1481         }
1482
1483         if (to_list) {
1484                 debug_print("Sending message by mail\n");
1485                 if (!from) {
1486                         g_warning("Queued message header is broken.\n");
1487                         mailval = -1;
1488                 } else if (mailac && mailac->use_mail_command &&
1489                            mailac->mail_command && (* mailac->mail_command)) {
1490                         mailval = send_message_local(mailac->mail_command, fp);
1491                         local = 1;
1492                 } else {
1493                         if (!mailac) {
1494                                 mailac = account_find_from_smtp_server(from, smtpserver);
1495                                 if (!mailac) {
1496                                         g_warning("Account not found. "
1497                                                     "Using current account...\n");
1498                                         mailac = cur_account;
1499                                 }
1500                         }
1501
1502                         if (mailac)
1503                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1504                         else {
1505                                 PrefsAccount tmp_ac;
1506
1507                                 g_warning("Account not found.\n");
1508
1509                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1510                                 tmp_ac.address = from;
1511                                 tmp_ac.smtp_server = smtpserver;
1512                                 tmp_ac.smtpport = SMTP_PORT;
1513                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1514                         }
1515                 }
1516         }
1517
1518         fseek(fp, filepos, SEEK_SET);
1519         if (newsgroup_list && (mailval == 0)) {
1520                 Folder *folder;
1521                 gchar *tmp = NULL;
1522                 FILE *tmpfp;
1523
1524                 /* write to temporary file */
1525                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1526                             G_DIR_SEPARATOR, (gint)file);
1527                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1528                         FILE_OP_ERROR(tmp, "fopen");
1529                         newsval = -1;
1530                         alertpanel_error(_("Could not create temporary file for news sending."));
1531                 } else {
1532                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1533                                 FILE_OP_ERROR(tmp, "chmod");
1534                                 g_warning("can't change file mode\n");
1535                         }
1536
1537                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1538                                 if (fputs(buf, tmpfp) == EOF) {
1539                                         FILE_OP_ERROR(tmp, "fputs");
1540                                         newsval = -1;
1541                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1542                                 }
1543                         }
1544                         fclose(tmpfp);
1545
1546                         if (newsval == 0) {
1547                                 debug_print("Sending message by news\n");
1548
1549                                 folder = FOLDER(newsac->folder);
1550
1551                                 newsval = news_post(folder, tmp);
1552                                 if (newsval < 0) {
1553                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1554                                                  newsac->nntp_server);
1555                                 }
1556                         }
1557                         g_unlink(tmp);
1558                 }
1559                 g_free(tmp);
1560         }
1561
1562         fclose(fp);
1563
1564         /* save message to outbox */
1565         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1566                 FolderItem *outbox;
1567
1568                 debug_print("saving sent message...\n");
1569
1570                 outbox = folder_find_item_from_identifier(savecopyfolder);
1571                 if (!outbox)
1572                         outbox = folder_get_default_outbox();
1573                         
1574                 if (save_clear_text || tmp_enc_file == NULL) {
1575                         procmsg_save_to_outbox(outbox, file, TRUE);
1576                 } else {
1577                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1578                 }
1579         }
1580
1581         if (tmp_enc_file != NULL) {
1582                 g_unlink(tmp_enc_file);
1583                 free(tmp_enc_file);
1584                 tmp_enc_file = NULL;
1585         }
1586
1587         if (replymessageid != NULL || fwdmessageid != NULL) {
1588                 gchar **tokens;
1589                 FolderItem *item;
1590                 
1591                 if (replymessageid != NULL)
1592                         tokens = g_strsplit(replymessageid, "\x7f", 0);
1593                 else
1594                         tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1595                 item = folder_find_item_from_identifier(tokens[0]);
1596
1597                 /* check if queued message has valid folder and message id */
1598                 if (item != NULL && tokens[2] != NULL) {
1599                         MsgInfo *msginfo;
1600                         
1601                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1602                 
1603                         /* check if referring message exists and has a message id */
1604                         if ((msginfo != NULL) && 
1605                             (msginfo->msgid != NULL) &&
1606                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1607                                 procmsg_msginfo_free(msginfo);
1608                                 msginfo = NULL;
1609                         }
1610                         
1611                         if (msginfo == NULL) {
1612                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1613                         }
1614                         
1615                         if (msginfo != NULL) {
1616                                 if (replymessageid != NULL) {
1617                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1618                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1619                                 }  else {
1620                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1621                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1622                                 }
1623                                 procmsg_msginfo_free(msginfo);
1624                         }
1625                 }
1626                 g_strfreev(tokens);
1627         }
1628
1629         g_free(from);
1630         g_free(smtpserver);
1631         slist_free_strings(to_list);
1632         g_slist_free(to_list);
1633         slist_free_strings(newsgroup_list);
1634         g_slist_free(newsgroup_list);
1635         g_free(savecopyfolder);
1636         g_free(replymessageid);
1637         g_free(fwdmessageid);
1638         g_free(privacy_system);
1639         g_free(encrypt_data);
1640
1641         return (newsval != 0 ? newsval : mailval);
1642 }
1643
1644 gint procmsg_send_message_queue(const gchar *file)
1645 {
1646         return procmsg_send_message_queue_full(file, FALSE);
1647 }
1648
1649 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1650 {
1651         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1652
1653         /* NEW flag */
1654         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1655                 item->new_msgs++;
1656         }
1657
1658         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1659                 item->new_msgs--;
1660         }
1661
1662         /* UNREAD flag */
1663         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1664                 item->unread_msgs++;
1665                 if (procmsg_msg_has_marked_parent(msginfo))
1666                         item->unreadmarked_msgs++;
1667         }
1668
1669         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1670                 item->unread_msgs--;
1671                 if (procmsg_msg_has_marked_parent(msginfo))
1672                         item->unreadmarked_msgs--;
1673         }
1674         
1675         /* MARK flag */
1676         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1677                 procmsg_update_unread_children(msginfo, TRUE);
1678                 item->marked_msgs++;
1679         }
1680
1681         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1682                 procmsg_update_unread_children(msginfo, FALSE);
1683                 item->marked_msgs--;
1684         }
1685 }
1686
1687 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1688 {
1689         FolderItem *item;
1690         MsgInfoUpdate msginfo_update;
1691         MsgPermFlags perm_flags_new, perm_flags_old;
1692         MsgTmpFlags tmp_flags_old;
1693
1694         g_return_if_fail(msginfo != NULL);
1695         item = msginfo->folder;
1696         g_return_if_fail(item != NULL);
1697         
1698         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1699
1700         /* Perm Flags handling */
1701         perm_flags_old = msginfo->flags.perm_flags;
1702         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1703         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1704                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1705         }
1706
1707         if (perm_flags_old != perm_flags_new) {
1708                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1709
1710                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1711
1712         }
1713
1714         /* Tmp flags handling */
1715         tmp_flags_old = msginfo->flags.tmp_flags;
1716         msginfo->flags.tmp_flags |= tmp_flags;
1717
1718         /* update notification */
1719         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1720                 msginfo_update.msginfo = msginfo;
1721                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1722                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1723                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1724         }
1725 }
1726
1727 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1728 {
1729         FolderItem *item;
1730         MsgInfoUpdate msginfo_update;
1731         MsgPermFlags perm_flags_new, perm_flags_old;
1732         MsgTmpFlags tmp_flags_old;
1733
1734         g_return_if_fail(msginfo != NULL);
1735         item = msginfo->folder;
1736         g_return_if_fail(item != NULL);
1737         
1738         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1739
1740         /* Perm Flags handling */
1741         perm_flags_old = msginfo->flags.perm_flags;
1742         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1743         
1744         if (perm_flags_old != perm_flags_new) {
1745                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1746
1747                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1748
1749                 msginfo_update.msginfo = msginfo;
1750                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1751                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1752                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1753         }
1754
1755         /* Tmp flags hanlding */
1756         tmp_flags_old = msginfo->flags.tmp_flags;
1757         msginfo->flags.tmp_flags &= ~tmp_flags;
1758
1759         /* update notification */
1760         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1761                 msginfo_update.msginfo = msginfo;
1762                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1763                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1764                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1765         }
1766 }
1767
1768 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1769                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1770                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1771 {
1772         FolderItem *item;
1773         MsgInfoUpdate msginfo_update;
1774         MsgPermFlags perm_flags_new, perm_flags_old;
1775         MsgTmpFlags tmp_flags_old;
1776
1777         g_return_if_fail(msginfo != NULL);
1778         item = msginfo->folder;
1779         g_return_if_fail(item != NULL);
1780         
1781         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1782
1783         /* Perm Flags handling */
1784         perm_flags_old = msginfo->flags.perm_flags;
1785         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1786         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1787                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1788         }
1789
1790         if (perm_flags_old != perm_flags_new) {
1791                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1792
1793                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1794
1795         }
1796
1797         /* Tmp flags handling */
1798         tmp_flags_old = msginfo->flags.tmp_flags;
1799         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1800         msginfo->flags.tmp_flags |= add_tmp_flags;
1801
1802         /* update notification */
1803         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1804                 msginfo_update.msginfo = msginfo;
1805                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1806                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1807                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1808         }
1809 }
1810
1811 /*!
1812  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1813  *
1814  *\param        info Current message
1815  *\param        perm_flags Flags to be checked
1816  *\param        parentmsgs Hash of prior msgs to avoid loops
1817  *
1818  *\return       gboolean TRUE if perm_flags are found
1819  */
1820 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1821                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1822 {
1823         MsgInfo *tmp;
1824
1825         g_return_val_if_fail(info != NULL, FALSE);
1826
1827         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1828                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1829                                 info->inreplyto);
1830                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1831                         procmsg_msginfo_free(tmp);
1832                         return TRUE;
1833                 } else if (tmp != NULL) {
1834                         gboolean result;
1835
1836                         if (g_hash_table_lookup(parentmsgs, info)) {
1837                                 debug_print("loop detected: %s%c%d\n",
1838                                         folder_item_get_path(info->folder),
1839                                         G_DIR_SEPARATOR, info->msgnum);
1840                                 result = FALSE;
1841                         } else {
1842                                 g_hash_table_insert(parentmsgs, info, "1");
1843                                 result = procmsg_msg_has_flagged_parent_real(
1844                                     tmp, perm_flags, parentmsgs);
1845                         }
1846                         procmsg_msginfo_free(tmp);
1847                         return result;
1848                 } else {
1849                         return FALSE;
1850                 }
1851         } else
1852                 return FALSE;
1853 }
1854
1855 /*!
1856  *\brief        Callback for cleaning up hash of parentmsgs
1857  */
1858 gboolean parentmsgs_hash_remove(gpointer key,
1859                             gpointer value,
1860                             gpointer user_data)
1861 {
1862         return TRUE;
1863 }
1864
1865 /*!
1866  *\brief        Set up list of parentmsgs
1867  *              See procmsg_msg_has_flagged_parent_real()
1868  */
1869 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1870 {
1871         gboolean result;
1872         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1873
1874         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1875         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1876         g_hash_table_destroy(parentmsgs);
1877         return result;
1878 }
1879
1880 /*!
1881  *\brief        Check if msgs prior in thread are marked
1882  *              See procmsg_msg_has_flagged_parent_real()
1883  */
1884 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1885 {
1886         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1887 }
1888
1889
1890 GSList *procmsg_find_children_func(MsgInfo *info, 
1891                                    GSList *children, GSList *all)
1892 {
1893         GSList *cur;
1894
1895         g_return_val_if_fail(info!=NULL, children);
1896         if (info->msgid == NULL)
1897                 return children;
1898
1899         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1900                 MsgInfo *tmp = (MsgInfo *)cur->data;
1901                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1902                         /* Check if message is already in the list */
1903                         if ((children == NULL) || 
1904                             (g_slist_index(children, tmp) == -1)) {
1905                                 children = g_slist_prepend(children,
1906                                                 procmsg_msginfo_new_ref(tmp));
1907                                 children = procmsg_find_children_func(tmp, 
1908                                                         children, 
1909                                                         all);
1910                         }
1911                 }
1912         }
1913         return children;
1914 }
1915
1916 GSList *procmsg_find_children (MsgInfo *info)
1917 {
1918         GSList *children;
1919         GSList *all, *cur;
1920
1921         g_return_val_if_fail(info!=NULL, NULL);
1922         all = folder_item_get_msg_list(info->folder);
1923         children = procmsg_find_children_func(info, NULL, all);
1924         if (children != NULL) {
1925                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1926                         /* this will not free the used pointers
1927                            created with procmsg_msginfo_new_ref */
1928                         procmsg_msginfo_free((MsgInfo *)cur->data);
1929                 }
1930         }
1931         g_slist_free(all);
1932
1933         return children;
1934 }
1935
1936 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1937 {
1938         GSList *children = procmsg_find_children(info);
1939         GSList *cur;
1940         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1941                 MsgInfo *tmp = (MsgInfo *)cur->data;
1942                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1943                         if(newly_marked) 
1944                                 info->folder->unreadmarked_msgs++;
1945                         else
1946                                 info->folder->unreadmarked_msgs--;
1947                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1948                 }
1949                 procmsg_msginfo_free(tmp);
1950         }
1951         g_slist_free(children);
1952 }
1953
1954 /**
1955  * Set the destination folder for a copy or move operation
1956  *
1957  * \param msginfo The message which's destination folder is changed
1958  * \param to_folder The destination folder for the operation
1959  */
1960 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1961 {
1962         if(msginfo->to_folder != NULL) {
1963                 msginfo->to_folder->op_count--;
1964                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1965         }
1966         msginfo->to_folder = to_folder;
1967         if(to_folder != NULL) {
1968                 to_folder->op_count++;
1969                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1970         }
1971 }
1972
1973 /**
1974  * Apply filtering actions to the msginfo
1975  *
1976  * \param msginfo The MsgInfo describing the message that should be filtered
1977  * \return TRUE if the message was moved and MsgInfo is now invalid,
1978  *         FALSE otherwise
1979  */
1980 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1981 {
1982         MailFilteringData mail_filtering_data;
1983                         
1984         mail_filtering_data.msginfo = msginfo;                  
1985         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
1986                 return TRUE;
1987         }
1988
1989         /* filter if enabled in prefs or move to inbox if not */
1990         if((filtering_rules != NULL) &&
1991            filter_message_by_msginfo(filtering_rules, msginfo)) {
1992                 return TRUE;
1993         }
1994                 
1995         return FALSE;
1996 }
1997
1998 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1999 {
2000         MsgInfo *tmp_msginfo = NULL;
2001         MsgFlags flags = {0, 0};
2002         gchar *tmpfile = get_tmp_file();
2003         FILE *fp = g_fopen(tmpfile, "wb");
2004         
2005         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2006             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2007                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2008                 if (fp) 
2009                         fclose(fp);
2010                 g_free(tmpfile);
2011                 return NULL;
2012         }
2013         
2014         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2015                 fclose(fp);
2016                 fp = NULL;
2017                 tmp_msginfo = procheader_parse_file(
2018                         tmpfile, flags, 
2019                         TRUE, FALSE);
2020         }
2021         if (fp)
2022                 fclose(fp);
2023
2024         if (tmp_msginfo != NULL) {
2025                 if (src_msginfo)
2026                         tmp_msginfo->folder = src_msginfo->folder;
2027                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2028         } else {
2029                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2030         }
2031
2032         g_free(tmpfile);
2033
2034         return tmp_msginfo;
2035 }
2036
2037 static GSList *spam_learners = NULL;
2038
2039 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2040 {
2041         if (!g_slist_find(spam_learners, learn_func))
2042                 spam_learners = g_slist_append(spam_learners, learn_func);
2043         if (mainwindow_get_mainwindow()) {
2044                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2045                 summary_set_menu_sensitive(
2046                         mainwindow_get_mainwindow()->summaryview);
2047                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2048         }
2049 }
2050
2051 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2052 {
2053         spam_learners = g_slist_remove(spam_learners, learn_func);
2054         if (mainwindow_get_mainwindow()) {
2055                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2056                 summary_set_menu_sensitive(
2057                         mainwindow_get_mainwindow()->summaryview);
2058                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2059         }
2060 }
2061
2062 gboolean procmsg_spam_can_learn(void)
2063 {
2064         return g_slist_length(spam_learners) > 0;
2065 }
2066
2067 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2068 {
2069         GSList *cur = spam_learners;
2070         int ret = 0;
2071         for (; cur; cur = cur->next) {
2072                 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2073                 ret |= func(info, list, spam);
2074         }
2075         return ret;
2076 }
2077
2078 static gchar *spam_folder_item = NULL;
2079 void procmsg_spam_set_folder (const char *item_identifier)
2080 {
2081         if (spam_folder_item)
2082                 g_free(spam_folder_item);
2083         if (item_identifier)
2084                 spam_folder_item = g_strdup(item_identifier);
2085         else
2086                 spam_folder_item = NULL;
2087 }
2088
2089 FolderItem *procmsg_spam_get_folder (void)
2090 {
2091         FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2092         return item ? item : folder_get_default_trash();
2093 }