2005-09-16 [colin] 1.9.14cvs34
[claws.git] / src / procmsg.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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(xface);
1156         MEMBDUP(dispositionnotificationto);
1157         MEMBDUP(returnreceiptto);
1158
1159         refs = msginfo->references;
1160         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1161                 newmsginfo->references = g_slist_prepend
1162                         (newmsginfo->references, g_strdup(refs->data)); 
1163         }
1164         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1165
1166         MEMBCOPY(score);
1167         MEMBCOPY(threadscore);
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->dispositionnotificationto)
1199                 msginfo->dispositionnotificationto = 
1200                         g_strdup(full_msginfo->dispositionnotificationto);
1201         if (!msginfo->returnreceiptto)
1202                 msginfo->returnreceiptto = g_strdup
1203                         (full_msginfo->returnreceiptto);
1204         if (!msginfo->partial_recv && full_msginfo->partial_recv)
1205                 msginfo->partial_recv = g_strdup
1206                         (full_msginfo->partial_recv);
1207         msginfo->total_size = full_msginfo->total_size;
1208         if (!msginfo->account_server && full_msginfo->account_server)
1209                 msginfo->account_server = g_strdup
1210                         (full_msginfo->account_server);
1211         if (!msginfo->account_login && full_msginfo->account_login)
1212                 msginfo->account_login = g_strdup
1213                         (full_msginfo->account_login);
1214         msginfo->planned_download = full_msginfo->planned_download;
1215         procmsg_msginfo_free(full_msginfo);
1216
1217         return procmsg_msginfo_new_ref(msginfo);
1218 }
1219
1220 void procmsg_msginfo_free(MsgInfo *msginfo)
1221 {
1222         if (msginfo == NULL) return;
1223
1224         msginfo->refcnt--;
1225         if (msginfo->refcnt > 0)
1226                 return;
1227
1228         if (msginfo->to_folder) {
1229                 msginfo->to_folder->op_count--;
1230                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1231         }
1232
1233         g_free(msginfo->fromspace);
1234         g_free(msginfo->returnreceiptto);
1235         g_free(msginfo->dispositionnotificationto);
1236         g_free(msginfo->xface);
1237
1238         g_free(msginfo->fromname);
1239
1240         g_free(msginfo->date);
1241         g_free(msginfo->from);
1242         g_free(msginfo->to);
1243         g_free(msginfo->cc);
1244         g_free(msginfo->newsgroups);
1245         g_free(msginfo->subject);
1246         g_free(msginfo->msgid);
1247         g_free(msginfo->inreplyto);
1248         g_free(msginfo->xref);
1249
1250         g_free(msginfo->partial_recv);
1251         g_free(msginfo->account_server);
1252         g_free(msginfo->account_login);
1253         
1254         slist_free_strings(msginfo->references);
1255         g_slist_free(msginfo->references);
1256
1257         g_free(msginfo->plaintext_file);
1258
1259         g_free(msginfo);
1260 }
1261
1262 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1263 {
1264         guint memusage = 0;
1265         GSList *refs;
1266         
1267         memusage += sizeof(MsgInfo);
1268         if (msginfo->fromname)
1269                 memusage += strlen(msginfo->fromname);
1270         if (msginfo->date)
1271                 memusage += strlen(msginfo->date);
1272         if (msginfo->from)
1273                 memusage += strlen(msginfo->from);
1274         if (msginfo->to)
1275                 memusage += strlen(msginfo->to);
1276         if (msginfo->cc)
1277                 memusage += strlen(msginfo->cc);
1278         if (msginfo->newsgroups)
1279                 memusage += strlen(msginfo->newsgroups);
1280         if (msginfo->subject)
1281                 memusage += strlen(msginfo->subject);
1282         if (msginfo->msgid)
1283                 memusage += strlen(msginfo->msgid);
1284         if (msginfo->inreplyto)
1285                 memusage += strlen(msginfo->inreplyto);
1286         if (msginfo->xface)
1287                 memusage += strlen(msginfo->xface);
1288         if (msginfo->dispositionnotificationto)
1289                 memusage += strlen(msginfo->dispositionnotificationto);
1290         if (msginfo->returnreceiptto)
1291                 memusage += strlen(msginfo->returnreceiptto);
1292         for (refs = msginfo->references; refs; refs=refs->next) {
1293                 gchar *r = (gchar *)refs->data;
1294                 memusage += r?strlen(r):0;
1295         }
1296         if (msginfo->fromspace)
1297                 memusage += strlen(msginfo->fromspace);
1298
1299         return memusage;
1300 }
1301
1302 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1303 {
1304         const MsgInfo *msginfo1 = a;
1305         const MsgInfo *msginfo2 = b;
1306
1307         if (!msginfo1)
1308                 return -1;
1309         if (!msginfo2)
1310                 return -1;
1311
1312         return msginfo1->msgnum - msginfo2->msgnum;
1313 }
1314
1315 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1316 {
1317         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1318                                        {"SSV:",  NULL, FALSE},
1319                                        {"R:",    NULL, FALSE},
1320                                        {"NG:",   NULL, FALSE},
1321                                        {"MAID:", NULL, FALSE},
1322                                        {"NAID:", NULL, FALSE},
1323                                        {"SCF:",  NULL, FALSE},
1324                                        {"RMID:", NULL, FALSE},
1325                                        {"FMID:", NULL, FALSE},
1326                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1327                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1328                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1329                                        {NULL,    NULL, FALSE}};
1330         FILE *fp;
1331         gint filepos;
1332         gint mailval = 0, newsval = 0;
1333         gchar *from = NULL;
1334         gchar *smtpserver = NULL;
1335         GSList *to_list = NULL;
1336         GSList *newsgroup_list = NULL;
1337         gchar *savecopyfolder = NULL;
1338         gchar *replymessageid = NULL;
1339         gchar *fwdmessageid = NULL;
1340         gchar *privacy_system = NULL;
1341         gboolean encrypt = FALSE;
1342         gchar *encrypt_data = NULL;
1343         gchar buf[BUFFSIZE];
1344         gint hnum;
1345         PrefsAccount *mailac = NULL, *newsac = NULL;
1346         gboolean save_clear_text = TRUE;
1347         gchar *tmp_enc_file = NULL;
1348
1349         int local = 0;
1350
1351         g_return_val_if_fail(file != NULL, -1);
1352
1353         if ((fp = g_fopen(file, "rb")) == NULL) {
1354                 FILE_OP_ERROR(file, "fopen");
1355                 return -1;
1356         }
1357
1358         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1359                != -1) {
1360                 gchar *p = buf + strlen(qentry[hnum].name);
1361
1362                 switch (hnum) {
1363                 case Q_SENDER:
1364                         if (from == NULL) 
1365                                 from = g_strdup(p);
1366                         break;
1367                 case Q_SMTPSERVER:
1368                         if (smtpserver == NULL) 
1369                                 smtpserver = g_strdup(p);
1370                         break;
1371                 case Q_RECIPIENTS:
1372                         to_list = address_list_append(to_list, p);
1373                         break;
1374                 case Q_NEWSGROUPS:
1375                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1376                         break;
1377                 case Q_MAIL_ACCOUNT_ID:
1378                         mailac = account_find_from_id(atoi(p));
1379                         break;
1380                 case Q_NEWS_ACCOUNT_ID:
1381                         newsac = account_find_from_id(atoi(p));
1382                         break;
1383                 case Q_SAVE_COPY_FOLDER:
1384                         if (savecopyfolder == NULL) 
1385                                 savecopyfolder = g_strdup(p);
1386                         break;
1387                 case Q_REPLY_MESSAGE_ID:
1388                         if (replymessageid == NULL) 
1389                                 replymessageid = g_strdup(p);
1390                         break;
1391                 case Q_FWD_MESSAGE_ID:
1392                         if (fwdmessageid == NULL) 
1393                                 fwdmessageid = g_strdup(p);
1394                         break;
1395                 case Q_PRIVACY_SYSTEM:
1396                         if (privacy_system == NULL) 
1397                                 privacy_system = g_strdup(p);
1398                         break;
1399                 case Q_ENCRYPT:
1400                         if (p[0] == '1') 
1401                                 encrypt = TRUE;
1402                         break;
1403                 case Q_ENCRYPT_DATA:
1404                         if (encrypt_data == NULL) 
1405                                 encrypt_data = g_strdup(p);
1406                         break;
1407                 }
1408         }
1409         filepos = ftell(fp);
1410
1411         if (encrypt) {
1412                 MimeInfo *mimeinfo;
1413
1414                 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1415
1416                 fclose(fp);
1417                 fp = NULL;
1418
1419                 mimeinfo = procmime_scan_queue_file(file);
1420                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1421                 || (fp = my_tmpfile()) == NULL
1422                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1423                         if (fp)
1424                                 fclose(fp);
1425                         procmime_mimeinfo_free_all(mimeinfo);
1426                         g_free(from);
1427                         g_free(smtpserver);
1428                         slist_free_strings(to_list);
1429                         g_slist_free(to_list);
1430                         slist_free_strings(newsgroup_list);
1431                         g_slist_free(newsgroup_list);
1432                         g_free(savecopyfolder);
1433                         g_free(replymessageid);
1434                         g_free(fwdmessageid);
1435                         g_free(privacy_system);
1436                         g_free(encrypt_data);
1437                         return -1;
1438                 }
1439                 
1440                 rewind(fp);
1441                 if (!save_clear_text) {
1442                         gchar *content = NULL;
1443                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1444                         if (tmpfp) {
1445                                 fclose(tmpfp);
1446
1447                                 content = file_read_stream_to_str(fp);
1448                                 rewind(fp);
1449
1450                                 str_write_to_file(content, tmp_enc_file);
1451                                 g_free(content);
1452                         } else {
1453                                 g_warning("couldn't get tempfile\n");
1454                         }
1455                 } 
1456                 
1457                 procmime_mimeinfo_free_all(mimeinfo);
1458                 
1459                 filepos = 0;
1460         }
1461
1462         if (to_list) {
1463                 debug_print("Sending message by mail\n");
1464                 if (!from) {
1465                         g_warning("Queued message header is broken.\n");
1466                         mailval = -1;
1467                 } else if (mailac && mailac->use_mail_command &&
1468                            mailac->mail_command && (* mailac->mail_command)) {
1469                         mailval = send_message_local(mailac->mail_command, fp);
1470                         local = 1;
1471                 } else {
1472                         if (!mailac) {
1473                                 mailac = account_find_from_smtp_server(from, smtpserver);
1474                                 if (!mailac) {
1475                                         g_warning("Account not found. "
1476                                                     "Using current account...\n");
1477                                         mailac = cur_account;
1478                                 }
1479                         }
1480
1481                         if (mailac)
1482                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1483                         else {
1484                                 PrefsAccount tmp_ac;
1485
1486                                 g_warning("Account not found.\n");
1487
1488                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1489                                 tmp_ac.address = from;
1490                                 tmp_ac.smtp_server = smtpserver;
1491                                 tmp_ac.smtpport = SMTP_PORT;
1492                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1493                         }
1494                 }
1495         }
1496
1497         fseek(fp, filepos, SEEK_SET);
1498         if (newsgroup_list && (mailval == 0)) {
1499                 Folder *folder;
1500                 gchar *tmp = NULL;
1501                 FILE *tmpfp;
1502
1503                 /* write to temporary file */
1504                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1505                             G_DIR_SEPARATOR, (gint)file);
1506                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1507                         FILE_OP_ERROR(tmp, "fopen");
1508                         newsval = -1;
1509                         alertpanel_error(_("Could not create temporary file for news sending."));
1510                 } else {
1511                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1512                                 FILE_OP_ERROR(tmp, "chmod");
1513                                 g_warning("can't change file mode\n");
1514                         }
1515
1516                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1517                                 if (fputs(buf, tmpfp) == EOF) {
1518                                         FILE_OP_ERROR(tmp, "fputs");
1519                                         newsval = -1;
1520                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1521                                 }
1522                         }
1523                         fclose(tmpfp);
1524
1525                         if (newsval == 0) {
1526                                 debug_print("Sending message by news\n");
1527
1528                                 folder = FOLDER(newsac->folder);
1529
1530                                 newsval = news_post(folder, tmp);
1531                                 if (newsval < 0) {
1532                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1533                                                  newsac->nntp_server);
1534                                 }
1535                         }
1536                         g_unlink(tmp);
1537                 }
1538                 g_free(tmp);
1539         }
1540
1541         fclose(fp);
1542
1543         /* save message to outbox */
1544         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1545                 FolderItem *outbox;
1546
1547                 debug_print("saving sent message...\n");
1548
1549                 outbox = folder_find_item_from_identifier(savecopyfolder);
1550                 if (!outbox)
1551                         outbox = folder_get_default_outbox();
1552                         
1553                 if (save_clear_text || tmp_enc_file == NULL) {
1554                         procmsg_save_to_outbox(outbox, file, TRUE);
1555                 } else {
1556                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1557                 }
1558         }
1559
1560         if (tmp_enc_file != NULL) {
1561                 g_unlink(tmp_enc_file);
1562                 free(tmp_enc_file);
1563                 tmp_enc_file = NULL;
1564         }
1565
1566         if (replymessageid != NULL || fwdmessageid != NULL) {
1567                 gchar **tokens;
1568                 FolderItem *item;
1569                 
1570                 if (replymessageid != NULL)
1571                         tokens = g_strsplit(replymessageid, "\x7f", 0);
1572                 else
1573                         tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1574                 item = folder_find_item_from_identifier(tokens[0]);
1575
1576                 /* check if queued message has valid folder and message id */
1577                 if (item != NULL && tokens[2] != NULL) {
1578                         MsgInfo *msginfo;
1579                         
1580                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1581                 
1582                         /* check if referring message exists and has a message id */
1583                         if ((msginfo != NULL) && 
1584                             (msginfo->msgid != NULL) &&
1585                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1586                                 procmsg_msginfo_free(msginfo);
1587                                 msginfo = NULL;
1588                         }
1589                         
1590                         if (msginfo == NULL) {
1591                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1592                         }
1593                         
1594                         if (msginfo != NULL) {
1595                                 if (replymessageid != NULL) {
1596                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1597                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1598                                 }  else {
1599                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1600                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1601                                 }
1602                                 procmsg_msginfo_free(msginfo);
1603                         }
1604                 }
1605                 g_strfreev(tokens);
1606         }
1607
1608         g_free(from);
1609         g_free(smtpserver);
1610         slist_free_strings(to_list);
1611         g_slist_free(to_list);
1612         slist_free_strings(newsgroup_list);
1613         g_slist_free(newsgroup_list);
1614         g_free(savecopyfolder);
1615         g_free(replymessageid);
1616         g_free(fwdmessageid);
1617         g_free(privacy_system);
1618         g_free(encrypt_data);
1619
1620         return (newsval != 0 ? newsval : mailval);
1621 }
1622
1623 gint procmsg_send_message_queue(const gchar *file)
1624 {
1625         return procmsg_send_message_queue_full(file, FALSE);
1626 }
1627
1628 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1629 {
1630         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1631
1632         /* NEW flag */
1633         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1634                 item->new_msgs++;
1635         }
1636
1637         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1638                 item->new_msgs--;
1639         }
1640
1641         /* UNREAD flag */
1642         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1643                 item->unread_msgs++;
1644                 if (procmsg_msg_has_marked_parent(msginfo))
1645                         item->unreadmarked_msgs++;
1646         }
1647
1648         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1649                 item->unread_msgs--;
1650                 if (procmsg_msg_has_marked_parent(msginfo))
1651                         item->unreadmarked_msgs--;
1652         }
1653         
1654         /* MARK flag */
1655         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1656                 procmsg_update_unread_children(msginfo, TRUE);
1657                 item->marked_msgs++;
1658         }
1659
1660         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1661                 procmsg_update_unread_children(msginfo, FALSE);
1662                 item->marked_msgs--;
1663         }
1664 }
1665
1666 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1667 {
1668         FolderItem *item;
1669         MsgInfoUpdate msginfo_update;
1670         MsgPermFlags perm_flags_new, perm_flags_old;
1671         MsgTmpFlags tmp_flags_old;
1672
1673         g_return_if_fail(msginfo != NULL);
1674         item = msginfo->folder;
1675         g_return_if_fail(item != NULL);
1676         
1677         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1678
1679         /* Perm Flags handling */
1680         perm_flags_old = msginfo->flags.perm_flags;
1681         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1682         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1683                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1684         }
1685
1686         if (perm_flags_old != perm_flags_new) {
1687                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1688
1689                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1690
1691         }
1692
1693         /* Tmp flags handling */
1694         tmp_flags_old = msginfo->flags.tmp_flags;
1695         msginfo->flags.tmp_flags |= tmp_flags;
1696
1697         /* update notification */
1698         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1699                 msginfo_update.msginfo = msginfo;
1700                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1701                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1702                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1703         }
1704 }
1705
1706 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1707 {
1708         FolderItem *item;
1709         MsgInfoUpdate msginfo_update;
1710         MsgPermFlags perm_flags_new, perm_flags_old;
1711         MsgTmpFlags tmp_flags_old;
1712
1713         g_return_if_fail(msginfo != NULL);
1714         item = msginfo->folder;
1715         g_return_if_fail(item != NULL);
1716         
1717         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1718
1719         /* Perm Flags handling */
1720         perm_flags_old = msginfo->flags.perm_flags;
1721         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1722         
1723         if (perm_flags_old != perm_flags_new) {
1724                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1725
1726                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1727
1728                 msginfo_update.msginfo = msginfo;
1729                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1730                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1731                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1732         }
1733
1734         /* Tmp flags hanlding */
1735         tmp_flags_old = msginfo->flags.tmp_flags;
1736         msginfo->flags.tmp_flags &= ~tmp_flags;
1737
1738         /* update notification */
1739         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1740                 msginfo_update.msginfo = msginfo;
1741                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1742                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1743                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1744         }
1745 }
1746
1747 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1748                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1749                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1750 {
1751         FolderItem *item;
1752         MsgInfoUpdate msginfo_update;
1753         MsgPermFlags perm_flags_new, perm_flags_old;
1754         MsgTmpFlags tmp_flags_old;
1755
1756         g_return_if_fail(msginfo != NULL);
1757         item = msginfo->folder;
1758         g_return_if_fail(item != NULL);
1759         
1760         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1761
1762         /* Perm Flags handling */
1763         perm_flags_old = msginfo->flags.perm_flags;
1764         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1765         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1766                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1767         }
1768
1769         if (perm_flags_old != perm_flags_new) {
1770                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1771
1772                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1773
1774         }
1775
1776         /* Tmp flags handling */
1777         tmp_flags_old = msginfo->flags.tmp_flags;
1778         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1779         msginfo->flags.tmp_flags |= add_tmp_flags;
1780
1781         /* update notification */
1782         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1783                 msginfo_update.msginfo = msginfo;
1784                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1785                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1786                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1787         }
1788 }
1789
1790 /*!
1791  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1792  *
1793  *\param        info Current message
1794  *\param        perm_flags Flags to be checked
1795  *\param        parentmsgs Hash of prior msgs to avoid loops
1796  *
1797  *\return       gboolean TRUE if perm_flags are found
1798  */
1799 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1800                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1801 {
1802         MsgInfo *tmp;
1803
1804         g_return_val_if_fail(info != NULL, FALSE);
1805
1806         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1807                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1808                                 info->inreplyto);
1809                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1810                         procmsg_msginfo_free(tmp);
1811                         return TRUE;
1812                 } else if (tmp != NULL) {
1813                         gboolean result;
1814
1815                         if (g_hash_table_lookup(parentmsgs, info)) {
1816                                 debug_print("loop detected: %s%c%d\n",
1817                                         folder_item_get_path(info->folder),
1818                                         G_DIR_SEPARATOR, info->msgnum);
1819                                 result = FALSE;
1820                         } else {
1821                                 g_hash_table_insert(parentmsgs, info, "1");
1822                                 result = procmsg_msg_has_flagged_parent_real(
1823                                     tmp, perm_flags, parentmsgs);
1824                         }
1825                         procmsg_msginfo_free(tmp);
1826                         return result;
1827                 } else {
1828                         return FALSE;
1829                 }
1830         } else
1831                 return FALSE;
1832 }
1833
1834 /*!
1835  *\brief        Callback for cleaning up hash of parentmsgs
1836  */
1837 gboolean parentmsgs_hash_remove(gpointer key,
1838                             gpointer value,
1839                             gpointer user_data)
1840 {
1841         return TRUE;
1842 }
1843
1844 /*!
1845  *\brief        Set up list of parentmsgs
1846  *              See procmsg_msg_has_flagged_parent_real()
1847  */
1848 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1849 {
1850         gboolean result;
1851         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1852
1853         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1854         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1855         g_hash_table_destroy(parentmsgs);
1856         return result;
1857 }
1858
1859 /*!
1860  *\brief        Check if msgs prior in thread are marked
1861  *              See procmsg_msg_has_flagged_parent_real()
1862  */
1863 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1864 {
1865         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1866 }
1867
1868
1869 GSList *procmsg_find_children_func(MsgInfo *info, 
1870                                    GSList *children, GSList *all)
1871 {
1872         GSList *cur;
1873
1874         g_return_val_if_fail(info!=NULL, children);
1875         if (info->msgid == NULL)
1876                 return children;
1877
1878         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1879                 MsgInfo *tmp = (MsgInfo *)cur->data;
1880                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1881                         /* Check if message is already in the list */
1882                         if ((children == NULL) || 
1883                             (g_slist_index(children, tmp) == -1)) {
1884                                 children = g_slist_prepend(children,
1885                                                 procmsg_msginfo_new_ref(tmp));
1886                                 children = procmsg_find_children_func(tmp, 
1887                                                         children, 
1888                                                         all);
1889                         }
1890                 }
1891         }
1892         return children;
1893 }
1894
1895 GSList *procmsg_find_children (MsgInfo *info)
1896 {
1897         GSList *children;
1898         GSList *all, *cur;
1899
1900         g_return_val_if_fail(info!=NULL, NULL);
1901         all = folder_item_get_msg_list(info->folder);
1902         children = procmsg_find_children_func(info, NULL, all);
1903         if (children != NULL) {
1904                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1905                         /* this will not free the used pointers
1906                            created with procmsg_msginfo_new_ref */
1907                         procmsg_msginfo_free((MsgInfo *)cur->data);
1908                 }
1909         }
1910         g_slist_free(all);
1911
1912         return children;
1913 }
1914
1915 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1916 {
1917         GSList *children = procmsg_find_children(info);
1918         GSList *cur;
1919         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1920                 MsgInfo *tmp = (MsgInfo *)cur->data;
1921                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1922                         if(newly_marked) 
1923                                 info->folder->unreadmarked_msgs++;
1924                         else
1925                                 info->folder->unreadmarked_msgs--;
1926                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1927                 }
1928                 procmsg_msginfo_free(tmp);
1929         }
1930         g_slist_free(children);
1931 }
1932
1933 /**
1934  * Set the destination folder for a copy or move operation
1935  *
1936  * \param msginfo The message which's destination folder is changed
1937  * \param to_folder The destination folder for the operation
1938  */
1939 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1940 {
1941         if(msginfo->to_folder != NULL) {
1942                 msginfo->to_folder->op_count--;
1943                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1944         }
1945         msginfo->to_folder = to_folder;
1946         if(to_folder != NULL) {
1947                 to_folder->op_count++;
1948                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1949         }
1950 }
1951
1952 /**
1953  * Apply filtering actions to the msginfo
1954  *
1955  * \param msginfo The MsgInfo describing the message that should be filtered
1956  * \return TRUE if the message was moved and MsgInfo is now invalid,
1957  *         FALSE otherwise
1958  */
1959 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1960 {
1961         MailFilteringData mail_filtering_data;
1962                         
1963         mail_filtering_data.msginfo = msginfo;                  
1964         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1965                 return TRUE;
1966
1967         /* filter if enabled in prefs or move to inbox if not */
1968         if((filtering_rules != NULL) &&
1969            filter_message_by_msginfo(filtering_rules, msginfo))
1970                 return TRUE;
1971                 
1972         hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1973
1974         return FALSE;
1975 }
1976
1977 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1978 {
1979         MsgInfo *tmp_msginfo = NULL;
1980         MsgFlags flags = {0, 0};
1981         gchar *tmpfile = get_tmp_file();
1982         FILE *fp = g_fopen(tmpfile, "wb");
1983         
1984         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1985             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1986                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1987                 if (fp) 
1988                         fclose(fp);
1989                 g_free(tmpfile);
1990                 return NULL;
1991         }
1992         
1993         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1994                 fclose(fp);
1995                 fp = NULL;
1996                 tmp_msginfo = procheader_parse_file(
1997                         tmpfile, flags, 
1998                         TRUE, FALSE);
1999         }
2000         if (fp)
2001                 fclose(fp);
2002
2003         if (tmp_msginfo != NULL) {
2004                 tmp_msginfo->folder = src_msginfo->folder;
2005                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2006         } else {
2007                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2008         }
2009
2010         g_free(tmpfile);
2011
2012         return tmp_msginfo;
2013 }