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