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