2006-02-05 [colin] 2.0.0cvs13
[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 }
985 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
986                             gboolean is_queued)
987 {
988         gint num;
989         MsgInfo *msginfo, *tmp_msginfo;
990         MsgFlags flag = {0, 0};
991
992         debug_print("saving sent message...\n");
993
994         if (!outbox)
995                 outbox = folder_get_default_outbox();
996         g_return_val_if_fail(outbox != NULL, -1);
997
998         /* remove queueing headers */
999         if (is_queued) {
1000                 gchar tmp[MAXPATHLEN + 1];
1001
1002                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1003                            get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1004                 
1005                 if (procmsg_remove_special_headers(file, tmp) !=0)
1006                         return -1;
1007
1008                 folder_item_scan(outbox);
1009                 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1010                         g_warning("can't save message\n");
1011                         g_unlink(tmp);
1012                         return -1;
1013                 }
1014         } else {
1015                 folder_item_scan(outbox);
1016                 if ((num = folder_item_add_msg
1017                         (outbox, file, &flag, FALSE)) < 0) {
1018                         g_warning("can't save message\n");
1019                         return -1;
1020                 }
1021                 return -1;
1022         }
1023         msginfo = folder_item_get_msginfo(outbox, num);         /* refcnt++ */
1024         tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);   /* refcnt++ */ 
1025         if (msginfo != NULL) {
1026                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1027                 procmsg_msginfo_free(msginfo);                  /* refcnt-- */
1028                 /* tmp_msginfo == msginfo */
1029                 if (tmp_msginfo && (msginfo->dispositionnotificationto || 
1030                     msginfo->returnreceiptto)) {
1031                         procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 
1032                         procmsg_msginfo_free(msginfo);          /* refcnt-- */
1033                 }       
1034         }
1035
1036         return 0;
1037 }
1038
1039 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1040 {
1041         static const gchar *def_cmd = "lpr %s";
1042         static guint id = 0;
1043         gchar *prtmp;
1044         FILE *tmpfp, *prfp;
1045         gchar buf[1024];
1046         gchar *p;
1047
1048         g_return_if_fail(msginfo);
1049
1050         if (procmime_msginfo_is_encrypted(msginfo))
1051                 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1052         else
1053                 tmpfp = procmime_get_first_text_content(msginfo);
1054         if (tmpfp == NULL) {
1055                 g_warning("Can't get text part\n");
1056                 return;
1057         }
1058
1059         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1060                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1061
1062         if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1063                 FILE_OP_ERROR(prtmp, "fopen");
1064                 g_free(prtmp);
1065                 fclose(tmpfp);
1066                 return;
1067         }
1068
1069         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1070         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1071         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
1072         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
1073         if (msginfo->newsgroups)
1074                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1075         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1076         fputc('\n', prfp);
1077
1078         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1079                 fputs(buf, prfp);
1080
1081         fclose(prfp);
1082         fclose(tmpfp);
1083
1084         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1085             !strchr(p + 2, '%'))
1086                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1087         else {
1088                 if (cmdline)
1089                         g_warning("Print command line is invalid: '%s'\n",
1090                                   cmdline);
1091                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1092         }
1093
1094         g_free(prtmp);
1095
1096         g_strchomp(buf);
1097         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1098         system(buf);
1099 }
1100
1101 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1102 {
1103         msginfo->refcnt++;
1104         
1105         return msginfo;
1106 }
1107
1108 MsgInfo *procmsg_msginfo_new(void)
1109 {
1110         MsgInfo *newmsginfo;
1111
1112         newmsginfo = g_new0(MsgInfo, 1);
1113         newmsginfo->refcnt = 1;
1114         
1115         return newmsginfo;
1116 }
1117
1118 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1119 {
1120         MsgInfo *newmsginfo;
1121         GSList *refs;
1122
1123         if (msginfo == NULL) return NULL;
1124
1125         newmsginfo = g_new0(MsgInfo, 1);
1126
1127         newmsginfo->refcnt = 1;
1128
1129 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1130 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1131                         g_strdup(msginfo->mmb) : NULL
1132
1133         MEMBCOPY(msgnum);
1134         MEMBCOPY(size);
1135         MEMBCOPY(mtime);
1136         MEMBCOPY(date_t);
1137
1138         MEMBCOPY(flags);
1139
1140         MEMBDUP(fromname);
1141
1142         MEMBDUP(date);
1143         MEMBDUP(from);
1144         MEMBDUP(to);
1145         MEMBDUP(cc);
1146         MEMBDUP(newsgroups);
1147         MEMBDUP(subject);
1148         MEMBDUP(msgid);
1149         MEMBDUP(inreplyto);
1150         MEMBDUP(xref);
1151
1152         MEMBCOPY(folder);
1153         MEMBCOPY(to_folder);
1154
1155         MEMBDUP(face);
1156         MEMBDUP(xface);
1157         MEMBDUP(dispositionnotificationto);
1158         MEMBDUP(returnreceiptto);
1159
1160         refs = msginfo->references;
1161         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1162                 newmsginfo->references = g_slist_prepend
1163                         (newmsginfo->references, g_strdup(refs->data)); 
1164         }
1165         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1166
1167         MEMBCOPY(score);
1168         MEMBDUP(plaintext_file);
1169
1170         return newmsginfo;
1171 }
1172
1173 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1174 {
1175         MsgInfo *full_msginfo;
1176         gchar *file;
1177
1178         if (msginfo == NULL) return NULL;
1179
1180         file = procmsg_get_message_file_path(msginfo);
1181         if (!file || !is_file_exist(file)) {
1182                 g_free(file);
1183                 file = procmsg_get_message_file(msginfo);
1184         }
1185         if (!file || !is_file_exist(file)) {
1186                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1187                 return NULL;
1188         }
1189
1190         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1191         g_free(file);
1192         if (!full_msginfo) return NULL;
1193
1194         /* CLAWS: make sure we add the missing members; see: 
1195          * procheader.c::procheader_get_headernames() */
1196         if (!msginfo->xface)
1197                 msginfo->xface = g_strdup(full_msginfo->xface);
1198         if (!msginfo->face)
1199                 msginfo->face = g_strdup(full_msginfo->face);
1200         if (!msginfo->dispositionnotificationto)
1201                 msginfo->dispositionnotificationto = 
1202                         g_strdup(full_msginfo->dispositionnotificationto);
1203         if (!msginfo->returnreceiptto)
1204                 msginfo->returnreceiptto = g_strdup
1205                         (full_msginfo->returnreceiptto);
1206         if (!msginfo->partial_recv && full_msginfo->partial_recv)
1207                 msginfo->partial_recv = g_strdup
1208                         (full_msginfo->partial_recv);
1209         msginfo->total_size = full_msginfo->total_size;
1210         if (!msginfo->account_server && full_msginfo->account_server)
1211                 msginfo->account_server = g_strdup
1212                         (full_msginfo->account_server);
1213         if (!msginfo->account_login && full_msginfo->account_login)
1214                 msginfo->account_login = g_strdup
1215                         (full_msginfo->account_login);
1216         msginfo->planned_download = full_msginfo->planned_download;
1217         procmsg_msginfo_free(full_msginfo);
1218
1219         return procmsg_msginfo_new_ref(msginfo);
1220 }
1221
1222 void procmsg_msginfo_free(MsgInfo *msginfo)
1223 {
1224         if (msginfo == NULL) return;
1225
1226         msginfo->refcnt--;
1227         if (msginfo->refcnt > 0)
1228                 return;
1229
1230         if (msginfo->to_folder) {
1231                 msginfo->to_folder->op_count--;
1232                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1233         }
1234
1235         g_free(msginfo->fromspace);
1236         g_free(msginfo->returnreceiptto);
1237         g_free(msginfo->dispositionnotificationto);
1238         g_free(msginfo->xface);
1239         g_free(msginfo->face);
1240
1241         g_free(msginfo->fromname);
1242
1243         g_free(msginfo->date);
1244         g_free(msginfo->from);
1245         g_free(msginfo->to);
1246         g_free(msginfo->cc);
1247         g_free(msginfo->newsgroups);
1248         g_free(msginfo->subject);
1249         g_free(msginfo->msgid);
1250         g_free(msginfo->inreplyto);
1251         g_free(msginfo->xref);
1252
1253         g_free(msginfo->partial_recv);
1254         g_free(msginfo->account_server);
1255         g_free(msginfo->account_login);
1256         
1257         slist_free_strings(msginfo->references);
1258         g_slist_free(msginfo->references);
1259
1260         g_free(msginfo->plaintext_file);
1261
1262         g_free(msginfo);
1263 }
1264
1265 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1266 {
1267         guint memusage = 0;
1268         GSList *refs;
1269         
1270         memusage += sizeof(MsgInfo);
1271         if (msginfo->fromname)
1272                 memusage += strlen(msginfo->fromname);
1273         if (msginfo->date)
1274                 memusage += strlen(msginfo->date);
1275         if (msginfo->from)
1276                 memusage += strlen(msginfo->from);
1277         if (msginfo->to)
1278                 memusage += strlen(msginfo->to);
1279         if (msginfo->cc)
1280                 memusage += strlen(msginfo->cc);
1281         if (msginfo->newsgroups)
1282                 memusage += strlen(msginfo->newsgroups);
1283         if (msginfo->subject)
1284                 memusage += strlen(msginfo->subject);
1285         if (msginfo->msgid)
1286                 memusage += strlen(msginfo->msgid);
1287         if (msginfo->inreplyto)
1288                 memusage += strlen(msginfo->inreplyto);
1289         if (msginfo->xface)
1290                 memusage += strlen(msginfo->xface);
1291         if (msginfo->face)
1292                 memusage += strlen(msginfo->face);
1293         if (msginfo->dispositionnotificationto)
1294                 memusage += strlen(msginfo->dispositionnotificationto);
1295         if (msginfo->returnreceiptto)
1296                 memusage += strlen(msginfo->returnreceiptto);
1297         for (refs = msginfo->references; refs; refs=refs->next) {
1298                 gchar *r = (gchar *)refs->data;
1299                 memusage += r?strlen(r):0;
1300         }
1301         if (msginfo->fromspace)
1302                 memusage += strlen(msginfo->fromspace);
1303
1304         return memusage;
1305 }
1306
1307 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1308 {
1309         const MsgInfo *msginfo1 = a;
1310         const MsgInfo *msginfo2 = b;
1311
1312         if (!msginfo1)
1313                 return -1;
1314         if (!msginfo2)
1315                 return -1;
1316
1317         return msginfo1->msgnum - msginfo2->msgnum;
1318 }
1319
1320 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1321 {
1322         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1323                                        {"SSV:",  NULL, FALSE},
1324                                        {"R:",    NULL, FALSE},
1325                                        {"NG:",   NULL, FALSE},
1326                                        {"MAID:", NULL, FALSE},
1327                                        {"NAID:", NULL, FALSE},
1328                                        {"SCF:",  NULL, FALSE},
1329                                        {"RMID:", NULL, FALSE},
1330                                        {"FMID:", NULL, FALSE},
1331                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1332                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1333                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1334                                        {NULL,    NULL, FALSE}};
1335         FILE *fp;
1336         gint filepos;
1337         gint mailval = 0, newsval = 0;
1338         gchar *from = NULL;
1339         gchar *smtpserver = NULL;
1340         GSList *to_list = NULL;
1341         GSList *newsgroup_list = NULL;
1342         gchar *savecopyfolder = NULL;
1343         gchar *replymessageid = NULL;
1344         gchar *fwdmessageid = NULL;
1345         gchar *privacy_system = NULL;
1346         gboolean encrypt = FALSE;
1347         gchar *encrypt_data = NULL;
1348         gchar buf[BUFFSIZE];
1349         gint hnum;
1350         PrefsAccount *mailac = NULL, *newsac = NULL;
1351         gboolean save_clear_text = TRUE;
1352         gchar *tmp_enc_file = NULL;
1353
1354         int local = 0;
1355
1356         g_return_val_if_fail(file != NULL, -1);
1357
1358         if ((fp = g_fopen(file, "rb")) == NULL) {
1359                 FILE_OP_ERROR(file, "fopen");
1360                 return -1;
1361         }
1362
1363         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1364                != -1) {
1365                 gchar *p = buf + strlen(qentry[hnum].name);
1366
1367                 switch (hnum) {
1368                 case Q_SENDER:
1369                         if (from == NULL) 
1370                                 from = g_strdup(p);
1371                         break;
1372                 case Q_SMTPSERVER:
1373                         if (smtpserver == NULL) 
1374                                 smtpserver = g_strdup(p);
1375                         break;
1376                 case Q_RECIPIENTS:
1377                         to_list = address_list_append(to_list, p);
1378                         break;
1379                 case Q_NEWSGROUPS:
1380                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1381                         break;
1382                 case Q_MAIL_ACCOUNT_ID:
1383                         mailac = account_find_from_id(atoi(p));
1384                         break;
1385                 case Q_NEWS_ACCOUNT_ID:
1386                         newsac = account_find_from_id(atoi(p));
1387                         break;
1388                 case Q_SAVE_COPY_FOLDER:
1389                         if (savecopyfolder == NULL) 
1390                                 savecopyfolder = g_strdup(p);
1391                         break;
1392                 case Q_REPLY_MESSAGE_ID:
1393                         if (replymessageid == NULL) 
1394                                 replymessageid = g_strdup(p);
1395                         break;
1396                 case Q_FWD_MESSAGE_ID:
1397                         if (fwdmessageid == NULL) 
1398                                 fwdmessageid = g_strdup(p);
1399                         break;
1400                 case Q_PRIVACY_SYSTEM:
1401                         if (privacy_system == NULL) 
1402                                 privacy_system = g_strdup(p);
1403                         break;
1404                 case Q_ENCRYPT:
1405                         if (p[0] == '1') 
1406                                 encrypt = TRUE;
1407                         break;
1408                 case Q_ENCRYPT_DATA:
1409                         if (encrypt_data == NULL) 
1410                                 encrypt_data = g_strdup(p);
1411                         break;
1412                 }
1413         }
1414         filepos = ftell(fp);
1415
1416         if (encrypt) {
1417                 MimeInfo *mimeinfo;
1418
1419                 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1420
1421                 fclose(fp);
1422                 fp = NULL;
1423
1424                 mimeinfo = procmime_scan_queue_file(file);
1425                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1426                 || (fp = my_tmpfile()) == NULL
1427                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1428                         if (fp)
1429                                 fclose(fp);
1430                         procmime_mimeinfo_free_all(mimeinfo);
1431                         g_free(from);
1432                         g_free(smtpserver);
1433                         slist_free_strings(to_list);
1434                         g_slist_free(to_list);
1435                         slist_free_strings(newsgroup_list);
1436                         g_slist_free(newsgroup_list);
1437                         g_free(savecopyfolder);
1438                         g_free(replymessageid);
1439                         g_free(fwdmessageid);
1440                         g_free(privacy_system);
1441                         g_free(encrypt_data);
1442                         return -1;
1443                 }
1444                 
1445                 rewind(fp);
1446                 if (!save_clear_text) {
1447                         gchar *content = NULL;
1448                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1449                         if (tmpfp) {
1450                                 fclose(tmpfp);
1451
1452                                 content = file_read_stream_to_str(fp);
1453                                 rewind(fp);
1454
1455                                 str_write_to_file(content, tmp_enc_file);
1456                                 g_free(content);
1457                         } else {
1458                                 g_warning("couldn't get tempfile\n");
1459                         }
1460                 } 
1461                 
1462                 procmime_mimeinfo_free_all(mimeinfo);
1463                 
1464                 filepos = 0;
1465         }
1466
1467         if (to_list) {
1468                 debug_print("Sending message by mail\n");
1469                 if (!from) {
1470                         g_warning("Queued message header is broken.\n");
1471                         mailval = -1;
1472                 } else if (mailac && mailac->use_mail_command &&
1473                            mailac->mail_command && (* mailac->mail_command)) {
1474                         mailval = send_message_local(mailac->mail_command, fp);
1475                         local = 1;
1476                 } else {
1477                         if (!mailac) {
1478                                 mailac = account_find_from_smtp_server(from, smtpserver);
1479                                 if (!mailac) {
1480                                         g_warning("Account not found. "
1481                                                     "Using current account...\n");
1482                                         mailac = cur_account;
1483                                 }
1484                         }
1485
1486                         if (mailac)
1487                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1488                         else {
1489                                 PrefsAccount tmp_ac;
1490
1491                                 g_warning("Account not found.\n");
1492
1493                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1494                                 tmp_ac.address = from;
1495                                 tmp_ac.smtp_server = smtpserver;
1496                                 tmp_ac.smtpport = SMTP_PORT;
1497                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1498                         }
1499                 }
1500         }
1501
1502         fseek(fp, filepos, SEEK_SET);
1503         if (newsgroup_list && (mailval == 0)) {
1504                 Folder *folder;
1505                 gchar *tmp = NULL;
1506                 FILE *tmpfp;
1507
1508                 /* write to temporary file */
1509                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1510                             G_DIR_SEPARATOR, (gint)file);
1511                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1512                         FILE_OP_ERROR(tmp, "fopen");
1513                         newsval = -1;
1514                         alertpanel_error(_("Could not create temporary file for news sending."));
1515                 } else {
1516                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1517                                 FILE_OP_ERROR(tmp, "chmod");
1518                                 g_warning("can't change file mode\n");
1519                         }
1520
1521                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1522                                 if (fputs(buf, tmpfp) == EOF) {
1523                                         FILE_OP_ERROR(tmp, "fputs");
1524                                         newsval = -1;
1525                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1526                                 }
1527                         }
1528                         fclose(tmpfp);
1529
1530                         if (newsval == 0) {
1531                                 debug_print("Sending message by news\n");
1532
1533                                 folder = FOLDER(newsac->folder);
1534
1535                                 newsval = news_post(folder, tmp);
1536                                 if (newsval < 0) {
1537                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1538                                                  newsac->nntp_server);
1539                                 }
1540                         }
1541                         g_unlink(tmp);
1542                 }
1543                 g_free(tmp);
1544         }
1545
1546         fclose(fp);
1547
1548         /* save message to outbox */
1549         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1550                 FolderItem *outbox;
1551
1552                 debug_print("saving sent message...\n");
1553
1554                 outbox = folder_find_item_from_identifier(savecopyfolder);
1555                 if (!outbox)
1556                         outbox = folder_get_default_outbox();
1557                         
1558                 if (save_clear_text || tmp_enc_file == NULL) {
1559                         procmsg_save_to_outbox(outbox, file, TRUE);
1560                 } else {
1561                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1562                 }
1563         }
1564
1565         if (tmp_enc_file != NULL) {
1566                 g_unlink(tmp_enc_file);
1567                 free(tmp_enc_file);
1568                 tmp_enc_file = NULL;
1569         }
1570
1571         if (replymessageid != NULL || fwdmessageid != NULL) {
1572                 gchar **tokens;
1573                 FolderItem *item;
1574                 
1575                 if (replymessageid != NULL)
1576                         tokens = g_strsplit(replymessageid, "\x7f", 0);
1577                 else
1578                         tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1579                 item = folder_find_item_from_identifier(tokens[0]);
1580
1581                 /* check if queued message has valid folder and message id */
1582                 if (item != NULL && tokens[2] != NULL) {
1583                         MsgInfo *msginfo;
1584                         
1585                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1586                 
1587                         /* check if referring message exists and has a message id */
1588                         if ((msginfo != NULL) && 
1589                             (msginfo->msgid != NULL) &&
1590                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1591                                 procmsg_msginfo_free(msginfo);
1592                                 msginfo = NULL;
1593                         }
1594                         
1595                         if (msginfo == NULL) {
1596                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1597                         }
1598                         
1599                         if (msginfo != NULL) {
1600                                 if (replymessageid != NULL) {
1601                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1602                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1603                                 }  else {
1604                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1605                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1606                                 }
1607                                 procmsg_msginfo_free(msginfo);
1608                         }
1609                 }
1610                 g_strfreev(tokens);
1611         }
1612
1613         g_free(from);
1614         g_free(smtpserver);
1615         slist_free_strings(to_list);
1616         g_slist_free(to_list);
1617         slist_free_strings(newsgroup_list);
1618         g_slist_free(newsgroup_list);
1619         g_free(savecopyfolder);
1620         g_free(replymessageid);
1621         g_free(fwdmessageid);
1622         g_free(privacy_system);
1623         g_free(encrypt_data);
1624
1625         return (newsval != 0 ? newsval : mailval);
1626 }
1627
1628 gint procmsg_send_message_queue(const gchar *file)
1629 {
1630         return procmsg_send_message_queue_full(file, FALSE);
1631 }
1632
1633 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1634 {
1635         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1636
1637         /* NEW flag */
1638         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1639                 item->new_msgs++;
1640         }
1641
1642         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1643                 item->new_msgs--;
1644         }
1645
1646         /* UNREAD flag */
1647         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1648                 item->unread_msgs++;
1649                 if (procmsg_msg_has_marked_parent(msginfo))
1650                         item->unreadmarked_msgs++;
1651         }
1652
1653         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1654                 item->unread_msgs--;
1655                 if (procmsg_msg_has_marked_parent(msginfo))
1656                         item->unreadmarked_msgs--;
1657         }
1658         
1659         /* MARK flag */
1660         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1661                 procmsg_update_unread_children(msginfo, TRUE);
1662                 item->marked_msgs++;
1663         }
1664
1665         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1666                 procmsg_update_unread_children(msginfo, FALSE);
1667                 item->marked_msgs--;
1668         }
1669 }
1670
1671 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1672 {
1673         FolderItem *item;
1674         MsgInfoUpdate msginfo_update;
1675         MsgPermFlags perm_flags_new, perm_flags_old;
1676         MsgTmpFlags tmp_flags_old;
1677
1678         g_return_if_fail(msginfo != NULL);
1679         item = msginfo->folder;
1680         g_return_if_fail(item != NULL);
1681         
1682         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1683
1684         /* Perm Flags handling */
1685         perm_flags_old = msginfo->flags.perm_flags;
1686         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1687         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1688                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1689         }
1690
1691         if (perm_flags_old != perm_flags_new) {
1692                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1693
1694                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1695
1696         }
1697
1698         /* Tmp flags handling */
1699         tmp_flags_old = msginfo->flags.tmp_flags;
1700         msginfo->flags.tmp_flags |= tmp_flags;
1701
1702         /* update notification */
1703         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1704                 msginfo_update.msginfo = msginfo;
1705                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1706                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1707                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1708         }
1709 }
1710
1711 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1712 {
1713         FolderItem *item;
1714         MsgInfoUpdate msginfo_update;
1715         MsgPermFlags perm_flags_new, perm_flags_old;
1716         MsgTmpFlags tmp_flags_old;
1717
1718         g_return_if_fail(msginfo != NULL);
1719         item = msginfo->folder;
1720         g_return_if_fail(item != NULL);
1721         
1722         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1723
1724         /* Perm Flags handling */
1725         perm_flags_old = msginfo->flags.perm_flags;
1726         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1727         
1728         if (perm_flags_old != perm_flags_new) {
1729                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1730
1731                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1732
1733                 msginfo_update.msginfo = msginfo;
1734                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1735                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1736                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1737         }
1738
1739         /* Tmp flags hanlding */
1740         tmp_flags_old = msginfo->flags.tmp_flags;
1741         msginfo->flags.tmp_flags &= ~tmp_flags;
1742
1743         /* update notification */
1744         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1745                 msginfo_update.msginfo = msginfo;
1746                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1747                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1748                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1749         }
1750 }
1751
1752 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1753                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1754                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_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("Changing 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 & ~rem_perm_flags) | add_perm_flags;
1770         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1771                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1772         }
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         }
1780
1781         /* Tmp flags handling */
1782         tmp_flags_old = msginfo->flags.tmp_flags;
1783         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1784         msginfo->flags.tmp_flags |= add_tmp_flags;
1785
1786         /* update notification */
1787         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1788                 msginfo_update.msginfo = msginfo;
1789                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1790                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1791                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1792         }
1793 }
1794
1795 /*!
1796  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1797  *
1798  *\param        info Current message
1799  *\param        perm_flags Flags to be checked
1800  *\param        parentmsgs Hash of prior msgs to avoid loops
1801  *
1802  *\return       gboolean TRUE if perm_flags are found
1803  */
1804 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1805                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1806 {
1807         MsgInfo *tmp;
1808
1809         g_return_val_if_fail(info != NULL, FALSE);
1810
1811         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1812                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1813                                 info->inreplyto);
1814                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1815                         procmsg_msginfo_free(tmp);
1816                         return TRUE;
1817                 } else if (tmp != NULL) {
1818                         gboolean result;
1819
1820                         if (g_hash_table_lookup(parentmsgs, info)) {
1821                                 debug_print("loop detected: %s%c%d\n",
1822                                         folder_item_get_path(info->folder),
1823                                         G_DIR_SEPARATOR, info->msgnum);
1824                                 result = FALSE;
1825                         } else {
1826                                 g_hash_table_insert(parentmsgs, info, "1");
1827                                 result = procmsg_msg_has_flagged_parent_real(
1828                                     tmp, perm_flags, parentmsgs);
1829                         }
1830                         procmsg_msginfo_free(tmp);
1831                         return result;
1832                 } else {
1833                         return FALSE;
1834                 }
1835         } else
1836                 return FALSE;
1837 }
1838
1839 /*!
1840  *\brief        Callback for cleaning up hash of parentmsgs
1841  */
1842 gboolean parentmsgs_hash_remove(gpointer key,
1843                             gpointer value,
1844                             gpointer user_data)
1845 {
1846         return TRUE;
1847 }
1848
1849 /*!
1850  *\brief        Set up list of parentmsgs
1851  *              See procmsg_msg_has_flagged_parent_real()
1852  */
1853 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1854 {
1855         gboolean result;
1856         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1857
1858         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1859         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1860         g_hash_table_destroy(parentmsgs);
1861         return result;
1862 }
1863
1864 /*!
1865  *\brief        Check if msgs prior in thread are marked
1866  *              See procmsg_msg_has_flagged_parent_real()
1867  */
1868 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1869 {
1870         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1871 }
1872
1873
1874 GSList *procmsg_find_children_func(MsgInfo *info, 
1875                                    GSList *children, GSList *all)
1876 {
1877         GSList *cur;
1878
1879         g_return_val_if_fail(info!=NULL, children);
1880         if (info->msgid == NULL)
1881                 return children;
1882
1883         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1884                 MsgInfo *tmp = (MsgInfo *)cur->data;
1885                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1886                         /* Check if message is already in the list */
1887                         if ((children == NULL) || 
1888                             (g_slist_index(children, tmp) == -1)) {
1889                                 children = g_slist_prepend(children,
1890                                                 procmsg_msginfo_new_ref(tmp));
1891                                 children = procmsg_find_children_func(tmp, 
1892                                                         children, 
1893                                                         all);
1894                         }
1895                 }
1896         }
1897         return children;
1898 }
1899
1900 GSList *procmsg_find_children (MsgInfo *info)
1901 {
1902         GSList *children;
1903         GSList *all, *cur;
1904
1905         g_return_val_if_fail(info!=NULL, NULL);
1906         all = folder_item_get_msg_list(info->folder);
1907         children = procmsg_find_children_func(info, NULL, all);
1908         if (children != NULL) {
1909                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1910                         /* this will not free the used pointers
1911                            created with procmsg_msginfo_new_ref */
1912                         procmsg_msginfo_free((MsgInfo *)cur->data);
1913                 }
1914         }
1915         g_slist_free(all);
1916
1917         return children;
1918 }
1919
1920 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1921 {
1922         GSList *children = procmsg_find_children(info);
1923         GSList *cur;
1924         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1925                 MsgInfo *tmp = (MsgInfo *)cur->data;
1926                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1927                         if(newly_marked) 
1928                                 info->folder->unreadmarked_msgs++;
1929                         else
1930                                 info->folder->unreadmarked_msgs--;
1931                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1932                 }
1933                 procmsg_msginfo_free(tmp);
1934         }
1935         g_slist_free(children);
1936 }
1937
1938 /**
1939  * Set the destination folder for a copy or move operation
1940  *
1941  * \param msginfo The message which's destination folder is changed
1942  * \param to_folder The destination folder for the operation
1943  */
1944 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1945 {
1946         if(msginfo->to_folder != NULL) {
1947                 msginfo->to_folder->op_count--;
1948                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1949         }
1950         msginfo->to_folder = to_folder;
1951         if(to_folder != NULL) {
1952                 to_folder->op_count++;
1953                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1954         }
1955 }
1956
1957 /**
1958  * Apply filtering actions to the msginfo
1959  *
1960  * \param msginfo The MsgInfo describing the message that should be filtered
1961  * \return TRUE if the message was moved and MsgInfo is now invalid,
1962  *         FALSE otherwise
1963  */
1964 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1965 {
1966         MailFilteringData mail_filtering_data;
1967                         
1968         mail_filtering_data.msginfo = msginfo;                  
1969         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
1970                 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1971                 return TRUE;
1972         }
1973
1974         /* filter if enabled in prefs or move to inbox if not */
1975         if((filtering_rules != NULL) &&
1976            filter_message_by_msginfo(filtering_rules, msginfo)) {
1977                 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1978                 return TRUE;
1979         }
1980                 
1981         hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1982         return FALSE;
1983 }
1984
1985 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1986 {
1987         MsgInfo *tmp_msginfo = NULL;
1988         MsgFlags flags = {0, 0};
1989         gchar *tmpfile = get_tmp_file();
1990         FILE *fp = g_fopen(tmpfile, "wb");
1991         
1992         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1993             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1994                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1995                 if (fp) 
1996                         fclose(fp);
1997                 g_free(tmpfile);
1998                 return NULL;
1999         }
2000         
2001         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2002                 fclose(fp);
2003                 fp = NULL;
2004                 tmp_msginfo = procheader_parse_file(
2005                         tmpfile, flags, 
2006                         TRUE, FALSE);
2007         }
2008         if (fp)
2009                 fclose(fp);
2010
2011         if (tmp_msginfo != NULL) {
2012                 if (src_msginfo)
2013                         tmp_msginfo->folder = src_msginfo->folder;
2014                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2015         } else {
2016                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2017         }
2018
2019         g_free(tmpfile);
2020
2021         return tmp_msginfo;
2022 }