2005-09-08 [colin] 1.9.14cvs8
[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                                        {NULL,              NULL, FALSE}};
553         enum
554         {
555                 H_X_BEENTHERE    = 0,
556                 H_X_ML_NAME      = 1,
557                 H_X_LIST         = 2,
558                 H_X_MAILING_LIST = 3,
559                 H_LIST_ID        = 4,
560                 H_X_SEQUENCE     = 5
561         };
562
563         FILE *fp;
564
565         g_return_if_fail(msginfo != NULL);
566         g_return_if_fail(header != NULL);
567         g_return_if_fail(key != NULL);
568
569         *header = NULL;
570         *key = NULL;
571
572         switch (type) {
573         case FILTER_BY_NONE:
574                 return;
575         case FILTER_BY_AUTO:
576                 if ((fp = procmsg_open_message(msginfo)) == NULL)
577                         return;
578                 procheader_get_header_fields(fp, hentry);
579                 fclose(fp);
580
581 #define SET_FILTER_KEY(hstr, idx)       \
582 {                                       \
583         *header = g_strdup(hstr);       \
584         *key = hentry[idx].body;        \
585         hentry[idx].body = NULL;        \
586 }
587
588                 if (hentry[H_X_BEENTHERE].body != NULL) {
589                         SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
590                 } else if (hentry[H_X_ML_NAME].body != NULL) {
591                         SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
592                 } else if (hentry[H_X_LIST].body != NULL) {
593                         SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
594                 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
595                         SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
596                 } else if (hentry[H_LIST_ID].body != NULL) {
597                         SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
598                         extract_list_id_str(*key);
599                 } else if (hentry[H_X_SEQUENCE].body != NULL) {
600                         gchar *p;
601
602                         SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
603                         p = *key;
604                         while (*p != '\0') {
605                                 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
606                                 while (g_ascii_isspace(*p)) p++;
607                                 if (g_ascii_isdigit(*p)) {
608                                         *p = '\0';
609                                         break;
610                                 }
611                         }
612                         g_strstrip(*key);
613                 } else if (msginfo->subject) {
614                         *header = g_strdup("subject");
615                         *key = g_strdup(msginfo->subject);
616                 }
617
618 #undef SET_FILTER_KEY
619
620                 g_free(hentry[H_X_BEENTHERE].body);
621                 hentry[H_X_BEENTHERE].body = NULL;
622                 g_free(hentry[H_X_ML_NAME].body);
623                 hentry[H_X_ML_NAME].body = NULL;
624                 g_free(hentry[H_X_LIST].body);
625                 hentry[H_X_LIST].body = NULL;
626                 g_free(hentry[H_X_MAILING_LIST].body);
627                 hentry[H_X_MAILING_LIST].body = NULL;
628                 g_free(hentry[H_LIST_ID].body);
629                 hentry[H_LIST_ID].body = NULL;
630
631                 break;
632         case FILTER_BY_FROM:
633                 *header = g_strdup("from");
634                 *key = g_strdup(msginfo->from);
635                 break;
636         case FILTER_BY_TO:
637                 *header = g_strdup("to");
638                 *key = g_strdup(msginfo->to);
639                 break;
640         case FILTER_BY_SUBJECT:
641                 *header = g_strdup("subject");
642                 *key = g_strdup(msginfo->subject);
643                 break;
644         default:
645                 break;
646         }
647 }
648
649 void procmsg_empty_trash(FolderItem *trash)
650 {
651         GNode *node, *next;
652
653         if (!trash || 
654             (trash->stype != F_TRASH && 
655              !folder_has_parent_of_type(trash, F_TRASH)))
656                 return;
657
658         if (trash && trash->total_msgs > 0) {
659                 GSList *mlist = folder_item_get_msg_list(trash);
660                 GSList *cur;
661                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
662                         MsgInfo * msginfo = (MsgInfo *) cur->data;
663                         if (MSG_IS_LOCKED(msginfo->flags))
664                                 continue;
665                         if (msginfo->total_size != 0 && 
666                             msginfo->size != (off_t)msginfo->total_size)
667                                 partial_mark_for_delete(msginfo);
668
669                         procmsg_msginfo_free(msginfo);
670                 }
671                 g_slist_free(mlist);
672                 folder_item_remove_all_msg(trash);
673         }
674
675         if (!trash->node || !trash->node->children)
676                 return;
677
678         node = trash->node->children;
679         while (node != NULL) {
680                 next = node->next;
681                 procmsg_empty_trash(FOLDER_ITEM(node->data));
682                 node = next;
683         }
684 }
685
686 void procmsg_empty_all_trash(void)
687 {
688         FolderItem *trash;
689         GList *cur;
690
691         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
692                 Folder *folder = FOLDER(cur->data);
693                 trash = folder->trash;
694                 procmsg_empty_trash(trash);
695                 if (folder->account && folder->account->set_trash_folder && 
696                     folder_find_item_from_identifier(folder->account->trash_folder))
697                         procmsg_empty_trash(
698                                 folder_find_item_from_identifier(folder->account->trash_folder));
699         }
700 }
701
702 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
703 {
704         PrefsAccount *mailac = NULL;
705         FILE *fp;
706         int hnum;
707         gchar buf[BUFFSIZE];
708         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
709                                        {"SSV:",  NULL, FALSE},
710                                        {"R:",    NULL, FALSE},
711                                        {"NG:",   NULL, FALSE},
712                                        {"MAID:", NULL, FALSE},
713                                        {"NAID:", NULL, FALSE},
714                                        {"SCF:",  NULL, FALSE},
715                                        {"RMID:", NULL, FALSE},
716                                        {"FMID:", NULL, FALSE},
717                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
718                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
719                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
720                                        {NULL,    NULL, FALSE}};
721         
722         g_return_val_if_fail(file != NULL, NULL);
723
724         if ((fp = g_fopen(file, "rb")) == NULL) {
725                 FILE_OP_ERROR(file, "fopen");
726                 return NULL;
727         }
728
729         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
730                != -1) {
731                 gchar *p = buf + strlen(qentry[hnum].name);
732
733                 if (hnum == Q_MAIL_ACCOUNT_ID) {
734                         mailac = account_find_from_id(atoi(p));
735                         break;
736                 }
737         }
738         fclose(fp);
739         return mailac;
740 }
741
742 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
743 {
744         GSList *result = NULL;
745         GSList *orig = NULL;
746         PrefsAccount *last_account = NULL;
747         MsgInfo *msg = NULL;
748         GSList *cur = NULL;
749         gboolean nothing_to_sort = TRUE;
750
751         if (!list)
752                 return NULL;
753
754         orig = g_slist_copy(list);
755         
756         msg = (MsgInfo *)orig->data;
757         
758         for (cur = orig; cur; cur = cur->next)
759                 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
760         
761         debug_print("\n");
762
763 parse_again:    
764         nothing_to_sort = TRUE;
765         cur = orig;
766         while (cur) {
767                 gchar *file = NULL;
768                 PrefsAccount *ac = NULL;
769                 msg = (MsgInfo *)cur->data;
770                 file = folder_item_fetch_msg(queue, msg->msgnum);
771                 ac = procmsg_get_account_from_file(file);
772                 g_free(file);
773
774                 if (last_account == NULL || (ac != NULL && ac == last_account)) {
775                         result = g_slist_append(result, msg);
776                         orig = g_slist_remove(orig, msg);
777                         last_account = ac;
778                         nothing_to_sort = FALSE;
779                         goto parse_again;
780                 }
781                 cur = cur->next;
782         }
783         
784         if (orig || g_slist_length(orig)) {
785                 if (!last_account && nothing_to_sort) {
786                         /* can't find an account for the rest of the list */
787                         cur = orig;
788                         while (cur) {
789                                 result = g_slist_append(result, cur->data);
790                                 cur = cur->next;
791                         }
792                 } else {
793                         last_account = NULL;
794                         goto parse_again;
795                 }
796         }
797         
798         g_slist_free(orig);
799         
800         for (cur = result; cur; cur = cur->next)
801                 debug_print("sort after  %s\n", ((MsgInfo *)cur->data)->from);
802
803         debug_print("\n");
804
805         return result;
806 }
807
808 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
809 {
810         gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
811         PrefsAccount *ac = procmsg_get_account_from_file(file);
812         GSList *cur = elem;
813         g_free(file);
814         for (cur = elem; cur; cur = cur->next) {
815                 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
816                 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
817                 
818                 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
819                         if (procmsg_get_account_from_file(file) == ac) {
820                                 g_free(file);
821                                 return FALSE;
822                         }
823                 }
824                 
825                 g_free(file);
826         }
827         return TRUE;
828 }
829
830 /*!
831  *\brief        Send messages in queue
832  *
833  *\param        queue Queue folder to process
834  *\param        save_msgs Unused
835  *
836  *\return       Number of messages sent, negative if an error occurred
837  *              positive if no error occurred
838  */
839 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
840 {
841         gint sent = 0, err = 0;
842         GSList *list, *elem;
843         GSList *sorted_list = NULL;
844         GNode *node, *next;
845
846         if (!queue)
847                 queue = folder_get_default_queue();
848         g_return_val_if_fail(queue != NULL, -1);
849
850         folder_item_scan(queue);
851         list = folder_item_get_msg_list(queue);
852
853         /* sort the list per sender account; this helps reusing the same SMTP server */
854         sorted_list = procmsg_list_sort_by_account(queue, list);
855         
856         for (elem = sorted_list; elem != NULL; elem = elem->next) {
857                 gchar *file;
858                 MsgInfo *msginfo;
859                         
860                 msginfo = (MsgInfo *)(elem->data);
861                 if (!MSG_IS_LOCKED(msginfo->flags)) {
862                         file = folder_item_fetch_msg(queue, msginfo->msgnum);
863                         if (file) {
864                                 if (procmsg_send_message_queue_full(file, 
865                                                 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
866                                         g_warning("Sending queued message %d failed.\n", 
867                                                   msginfo->msgnum);
868                                         err++;
869                                 } else {
870                                         /* CLAWS: 
871                                          * We save in procmsg_send_message_queue because
872                                          * we need the destination folder from the queue
873                                          * header
874                                                         
875                                         if (save_msgs)
876                                                 procmsg_save_to_outbox
877                                                         (queue->folder->outbox,
878                                                          file, TRUE);
879                                          */
880                                         sent++; 
881                                         folder_item_remove_msg(queue, msginfo->msgnum);
882                                 }
883                                 g_free(file);
884                         }
885                 }
886                 /* FIXME: supposedly if only one message is locked, and queue
887                  * is being flushed, the following free says something like 
888                  * "freeing msg ## in folder (nil)". */
889                 procmsg_msginfo_free(msginfo);
890         }
891
892         g_slist_free(sorted_list);
893         folder_item_scan(queue);
894
895         if (queue->node && queue->node->children) {
896                 node = queue->node->children;
897                 while (node != NULL) {
898                         int res = 0;
899                         next = node->next;
900                         res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
901                         if (res < 0) 
902                                 err = -res;
903                         else
904                                 sent += res;
905                         node = next;
906                 }
907         }
908
909         return (err != 0 ? -err : sent);
910 }
911
912 /*!
913  *\brief        Determine if a queue folder is empty
914  *
915  *\param        queue Queue folder to process
916  *
917  *\return       TRUE if the queue folder is empty, otherwise return FALSE
918  */
919 gboolean procmsg_queue_is_empty(FolderItem *queue)
920 {
921         GSList *list;
922         gboolean res = FALSE;
923         if (!queue)
924                 queue = folder_get_default_queue();
925         g_return_val_if_fail(queue != NULL, TRUE);
926
927         folder_item_scan(queue);
928         list = folder_item_get_msg_list(queue);
929         res = (list == NULL);
930         procmsg_msg_list_free(list);
931
932         if (res == TRUE) {
933                 GNode *node, *next;
934                 if (queue->node && queue->node->children) {
935                         node = queue->node->children;
936                         while (node != NULL) {
937                                 next = node->next;
938                                 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
939                                         return FALSE;
940                                 node = next;
941                         }
942                 }
943         }
944         return res;
945 }
946
947 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
948 {
949         FILE *fp, *outfp;
950         gchar buf[BUFFSIZE];
951         
952         if ((fp = g_fopen(in, "rb")) == NULL) {
953                 FILE_OP_ERROR(in, "fopen");
954                 return -1;
955         }
956         if ((outfp = g_fopen(out, "wb")) == NULL) {
957                 FILE_OP_ERROR(out, "fopen");
958                 fclose(fp);
959                 return -1;
960         }
961         while (fgets(buf, sizeof(buf), fp) != NULL)
962                 if (buf[0] == '\r' || buf[0] == '\n') break;
963         while (fgets(buf, sizeof(buf), fp) != NULL)
964                 fputs(buf, outfp);
965         fclose(outfp);
966         fclose(fp);
967         return 0;
968
969 }
970 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
971                             gboolean is_queued)
972 {
973         gint num;
974         MsgInfo *msginfo, *tmp_msginfo;
975         MsgFlags flag = {0, 0};
976
977         debug_print("saving sent message...\n");
978
979         if (!outbox)
980                 outbox = folder_get_default_outbox();
981         g_return_val_if_fail(outbox != NULL, -1);
982
983         /* remove queueing headers */
984         if (is_queued) {
985                 gchar tmp[MAXPATHLEN + 1];
986
987                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
988                            get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
989                 
990                 if (procmsg_remove_special_headers(file, tmp) !=0)
991                         return -1;
992
993                 folder_item_scan(outbox);
994                 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
995                         g_warning("can't save message\n");
996                         g_unlink(tmp);
997                         return -1;
998                 }
999         } else {
1000                 folder_item_scan(outbox);
1001                 if ((num = folder_item_add_msg
1002                         (outbox, file, &flag, FALSE)) < 0) {
1003                         g_warning("can't save message\n");
1004                         return -1;
1005                 }
1006                 return -1;
1007         }
1008         msginfo = folder_item_get_msginfo(outbox, num);         /* refcnt++ */
1009         tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);   /* refcnt++ */ 
1010         if (msginfo != NULL) {
1011                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1012                 procmsg_msginfo_free(msginfo);                  /* refcnt-- */
1013                 /* tmp_msginfo == msginfo */
1014                 if (tmp_msginfo && (msginfo->dispositionnotificationto || 
1015                     msginfo->returnreceiptto)) {
1016                         procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 
1017                         procmsg_msginfo_free(msginfo);          /* refcnt-- */
1018                 }       
1019         }
1020
1021         return 0;
1022 }
1023
1024 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1025 {
1026         static const gchar *def_cmd = "lpr %s";
1027         static guint id = 0;
1028         gchar *prtmp;
1029         FILE *tmpfp, *prfp;
1030         gchar buf[1024];
1031         gchar *p;
1032
1033         g_return_if_fail(msginfo);
1034
1035         if (procmime_msginfo_is_encrypted(msginfo))
1036                 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1037         else
1038                 tmpfp = procmime_get_first_text_content(msginfo);
1039         if (tmpfp == NULL) {
1040                 g_warning("Can't get text part\n");
1041                 return;
1042         }
1043
1044         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1045                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1046
1047         if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1048                 FILE_OP_ERROR(prtmp, "fopen");
1049                 g_free(prtmp);
1050                 fclose(tmpfp);
1051                 return;
1052         }
1053
1054         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1055         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1056         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
1057         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
1058         if (msginfo->newsgroups)
1059                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1060         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1061         fputc('\n', prfp);
1062
1063         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1064                 fputs(buf, prfp);
1065
1066         fclose(prfp);
1067         fclose(tmpfp);
1068
1069         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1070             !strchr(p + 2, '%'))
1071                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1072         else {
1073                 if (cmdline)
1074                         g_warning("Print command line is invalid: '%s'\n",
1075                                   cmdline);
1076                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1077         }
1078
1079         g_free(prtmp);
1080
1081         g_strchomp(buf);
1082         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1083         system(buf);
1084 }
1085
1086 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1087 {
1088         msginfo->refcnt++;
1089         
1090         return msginfo;
1091 }
1092
1093 MsgInfo *procmsg_msginfo_new(void)
1094 {
1095         MsgInfo *newmsginfo;
1096
1097         newmsginfo = g_new0(MsgInfo, 1);
1098         newmsginfo->refcnt = 1;
1099         
1100         return newmsginfo;
1101 }
1102
1103 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1104 {
1105         MsgInfo *newmsginfo;
1106         GSList *refs;
1107
1108         if (msginfo == NULL) return NULL;
1109
1110         newmsginfo = g_new0(MsgInfo, 1);
1111
1112         newmsginfo->refcnt = 1;
1113
1114 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1115 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1116                         g_strdup(msginfo->mmb) : NULL
1117
1118         MEMBCOPY(msgnum);
1119         MEMBCOPY(size);
1120         MEMBCOPY(mtime);
1121         MEMBCOPY(date_t);
1122
1123         MEMBCOPY(flags);
1124
1125         MEMBDUP(fromname);
1126
1127         MEMBDUP(date);
1128         MEMBDUP(from);
1129         MEMBDUP(to);
1130         MEMBDUP(cc);
1131         MEMBDUP(newsgroups);
1132         MEMBDUP(subject);
1133         MEMBDUP(msgid);
1134         MEMBDUP(inreplyto);
1135         MEMBDUP(xref);
1136
1137         MEMBCOPY(folder);
1138         MEMBCOPY(to_folder);
1139
1140         MEMBDUP(xface);
1141         MEMBDUP(dispositionnotificationto);
1142         MEMBDUP(returnreceiptto);
1143
1144         refs = msginfo->references;
1145         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1146                 newmsginfo->references = g_slist_prepend
1147                         (newmsginfo->references, g_strdup(refs->data)); 
1148         }
1149         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1150
1151         MEMBCOPY(score);
1152         MEMBCOPY(threadscore);
1153         MEMBDUP(plaintext_file);
1154
1155         return newmsginfo;
1156 }
1157
1158 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1159 {
1160         MsgInfo *full_msginfo;
1161         gchar *file;
1162
1163         if (msginfo == NULL) return NULL;
1164
1165         file = procmsg_get_message_file_path(msginfo);
1166         if (!file || !is_file_exist(file)) {
1167                 g_free(file);
1168                 file = procmsg_get_message_file(msginfo);
1169         }
1170         if (!file || !is_file_exist(file)) {
1171                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1172                 return NULL;
1173         }
1174
1175         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1176         g_free(file);
1177         if (!full_msginfo) return NULL;
1178
1179         /* CLAWS: make sure we add the missing members; see: 
1180          * procheader.c::procheader_get_headernames() */
1181         if (!msginfo->xface)
1182                 msginfo->xface = g_strdup(full_msginfo->xface);
1183         if (!msginfo->dispositionnotificationto)
1184                 msginfo->dispositionnotificationto = 
1185                         g_strdup(full_msginfo->dispositionnotificationto);
1186         if (!msginfo->returnreceiptto)
1187                 msginfo->returnreceiptto = g_strdup
1188                         (full_msginfo->returnreceiptto);
1189         if (!msginfo->partial_recv && full_msginfo->partial_recv)
1190                 msginfo->partial_recv = g_strdup
1191                         (full_msginfo->partial_recv);
1192         msginfo->total_size = full_msginfo->total_size;
1193         if (!msginfo->account_server && full_msginfo->account_server)
1194                 msginfo->account_server = g_strdup
1195                         (full_msginfo->account_server);
1196         if (!msginfo->account_login && full_msginfo->account_login)
1197                 msginfo->account_login = g_strdup
1198                         (full_msginfo->account_login);
1199         msginfo->planned_download = full_msginfo->planned_download;
1200         procmsg_msginfo_free(full_msginfo);
1201
1202         return procmsg_msginfo_new_ref(msginfo);
1203 }
1204
1205 void procmsg_msginfo_free(MsgInfo *msginfo)
1206 {
1207         if (msginfo == NULL) return;
1208
1209         msginfo->refcnt--;
1210         if (msginfo->refcnt > 0)
1211                 return;
1212
1213         if (msginfo->to_folder) {
1214                 msginfo->to_folder->op_count--;
1215                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1216         }
1217
1218         g_free(msginfo->fromspace);
1219         g_free(msginfo->returnreceiptto);
1220         g_free(msginfo->dispositionnotificationto);
1221         g_free(msginfo->xface);
1222
1223         g_free(msginfo->fromname);
1224
1225         g_free(msginfo->date);
1226         g_free(msginfo->from);
1227         g_free(msginfo->to);
1228         g_free(msginfo->cc);
1229         g_free(msginfo->newsgroups);
1230         g_free(msginfo->subject);
1231         g_free(msginfo->msgid);
1232         g_free(msginfo->inreplyto);
1233         g_free(msginfo->xref);
1234
1235         g_free(msginfo->partial_recv);
1236         g_free(msginfo->account_server);
1237         g_free(msginfo->account_login);
1238         
1239         slist_free_strings(msginfo->references);
1240         g_slist_free(msginfo->references);
1241
1242         g_free(msginfo->plaintext_file);
1243
1244         g_free(msginfo);
1245 }
1246
1247 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1248 {
1249         guint memusage = 0;
1250         GSList *refs;
1251         
1252         memusage += sizeof(MsgInfo);
1253         if (msginfo->fromname)
1254                 memusage += strlen(msginfo->fromname);
1255         if (msginfo->date)
1256                 memusage += strlen(msginfo->date);
1257         if (msginfo->from)
1258                 memusage += strlen(msginfo->from);
1259         if (msginfo->to)
1260                 memusage += strlen(msginfo->to);
1261         if (msginfo->cc)
1262                 memusage += strlen(msginfo->cc);
1263         if (msginfo->newsgroups)
1264                 memusage += strlen(msginfo->newsgroups);
1265         if (msginfo->subject)
1266                 memusage += strlen(msginfo->subject);
1267         if (msginfo->msgid)
1268                 memusage += strlen(msginfo->msgid);
1269         if (msginfo->inreplyto)
1270                 memusage += strlen(msginfo->inreplyto);
1271         if (msginfo->xface)
1272                 memusage += strlen(msginfo->xface);
1273         if (msginfo->dispositionnotificationto)
1274                 memusage += strlen(msginfo->dispositionnotificationto);
1275         if (msginfo->returnreceiptto)
1276                 memusage += strlen(msginfo->returnreceiptto);
1277         for (refs = msginfo->references; refs; refs=refs->next) {
1278                 gchar *r = (gchar *)refs->data;
1279                 memusage += r?strlen(r):0;
1280         }
1281         if (msginfo->fromspace)
1282                 memusage += strlen(msginfo->fromspace);
1283
1284         return memusage;
1285 }
1286
1287 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1288 {
1289         const MsgInfo *msginfo1 = a;
1290         const MsgInfo *msginfo2 = b;
1291
1292         if (!msginfo1)
1293                 return -1;
1294         if (!msginfo2)
1295                 return -1;
1296
1297         return msginfo1->msgnum - msginfo2->msgnum;
1298 }
1299
1300 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1301 {
1302         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1303                                        {"SSV:",  NULL, FALSE},
1304                                        {"R:",    NULL, FALSE},
1305                                        {"NG:",   NULL, FALSE},
1306                                        {"MAID:", NULL, FALSE},
1307                                        {"NAID:", NULL, FALSE},
1308                                        {"SCF:",  NULL, FALSE},
1309                                        {"RMID:", NULL, FALSE},
1310                                        {"FMID:", NULL, FALSE},
1311                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1312                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1313                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1314                                        {NULL,    NULL, FALSE}};
1315         FILE *fp;
1316         gint filepos;
1317         gint mailval = 0, newsval = 0;
1318         gchar *from = NULL;
1319         gchar *smtpserver = NULL;
1320         GSList *to_list = NULL;
1321         GSList *newsgroup_list = NULL;
1322         gchar *savecopyfolder = NULL;
1323         gchar *replymessageid = NULL;
1324         gchar *fwdmessageid = NULL;
1325         gchar *privacy_system = NULL;
1326         gboolean encrypt = FALSE;
1327         gchar *encrypt_data = NULL;
1328         gchar buf[BUFFSIZE];
1329         gint hnum;
1330         PrefsAccount *mailac = NULL, *newsac = NULL;
1331         gboolean save_clear_text = TRUE;
1332         gchar *tmp_enc_file = NULL;
1333
1334         int local = 0;
1335
1336         g_return_val_if_fail(file != NULL, -1);
1337
1338         if ((fp = g_fopen(file, "rb")) == NULL) {
1339                 FILE_OP_ERROR(file, "fopen");
1340                 return -1;
1341         }
1342
1343         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1344                != -1) {
1345                 gchar *p = buf + strlen(qentry[hnum].name);
1346
1347                 switch (hnum) {
1348                 case Q_SENDER:
1349                         if (from == NULL) 
1350                                 from = g_strdup(p);
1351                         break;
1352                 case Q_SMTPSERVER:
1353                         if (smtpserver == NULL) 
1354                                 smtpserver = g_strdup(p);
1355                         break;
1356                 case Q_RECIPIENTS:
1357                         to_list = address_list_append(to_list, p);
1358                         break;
1359                 case Q_NEWSGROUPS:
1360                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1361                         break;
1362                 case Q_MAIL_ACCOUNT_ID:
1363                         mailac = account_find_from_id(atoi(p));
1364                         break;
1365                 case Q_NEWS_ACCOUNT_ID:
1366                         newsac = account_find_from_id(atoi(p));
1367                         break;
1368                 case Q_SAVE_COPY_FOLDER:
1369                         if (savecopyfolder == NULL) 
1370                                 savecopyfolder = g_strdup(p);
1371                         break;
1372                 case Q_REPLY_MESSAGE_ID:
1373                         if (replymessageid == NULL) 
1374                                 replymessageid = g_strdup(p);
1375                         break;
1376                 case Q_FWD_MESSAGE_ID:
1377                         if (fwdmessageid == NULL) 
1378                                 fwdmessageid = g_strdup(p);
1379                         break;
1380                 case Q_PRIVACY_SYSTEM:
1381                         if (privacy_system == NULL) 
1382                                 privacy_system = g_strdup(p);
1383                         break;
1384                 case Q_ENCRYPT:
1385                         if (p[0] == '1') 
1386                                 encrypt = TRUE;
1387                         break;
1388                 case Q_ENCRYPT_DATA:
1389                         if (encrypt_data == NULL) 
1390                                 encrypt_data = g_strdup(p);
1391                         break;
1392                 }
1393         }
1394         filepos = ftell(fp);
1395
1396         if (encrypt) {
1397                 MimeInfo *mimeinfo;
1398
1399                 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1400
1401                 fclose(fp);
1402                 fp = NULL;
1403
1404                 mimeinfo = procmime_scan_queue_file(file);
1405                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1406                 || (fp = my_tmpfile()) == NULL
1407                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1408                         if (fp)
1409                                 fclose(fp);
1410                         procmime_mimeinfo_free_all(mimeinfo);
1411                         g_free(from);
1412                         g_free(smtpserver);
1413                         slist_free_strings(to_list);
1414                         g_slist_free(to_list);
1415                         slist_free_strings(newsgroup_list);
1416                         g_slist_free(newsgroup_list);
1417                         g_free(savecopyfolder);
1418                         g_free(replymessageid);
1419                         g_free(fwdmessageid);
1420                         g_free(privacy_system);
1421                         g_free(encrypt_data);
1422                         return -1;
1423                 }
1424                 
1425                 rewind(fp);
1426                 if (!save_clear_text) {
1427                         gchar *content = NULL;
1428                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1429                         if (tmpfp) {
1430                                 fclose(tmpfp);
1431
1432                                 content = file_read_stream_to_str(fp);
1433                                 rewind(fp);
1434
1435                                 str_write_to_file(content, tmp_enc_file);
1436                                 g_free(content);
1437                         } else {
1438                                 g_warning("couldn't get tempfile\n");
1439                         }
1440                 } 
1441                 
1442                 procmime_mimeinfo_free_all(mimeinfo);
1443                 
1444                 filepos = 0;
1445         }
1446
1447         if (to_list) {
1448                 debug_print("Sending message by mail\n");
1449                 if (!from) {
1450                         g_warning("Queued message header is broken.\n");
1451                         mailval = -1;
1452                 } else if (mailac && mailac->use_mail_command &&
1453                            mailac->mail_command && (* mailac->mail_command)) {
1454                         mailval = send_message_local(mailac->mail_command, fp);
1455                         local = 1;
1456                 } else {
1457                         if (!mailac) {
1458                                 mailac = account_find_from_smtp_server(from, smtpserver);
1459                                 if (!mailac) {
1460                                         g_warning("Account not found. "
1461                                                     "Using current account...\n");
1462                                         mailac = cur_account;
1463                                 }
1464                         }
1465
1466                         if (mailac)
1467                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1468                         else {
1469                                 PrefsAccount tmp_ac;
1470
1471                                 g_warning("Account not found.\n");
1472
1473                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1474                                 tmp_ac.address = from;
1475                                 tmp_ac.smtp_server = smtpserver;
1476                                 tmp_ac.smtpport = SMTP_PORT;
1477                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1478                         }
1479                 }
1480         }
1481
1482         fseek(fp, filepos, SEEK_SET);
1483         if (newsgroup_list && (mailval == 0)) {
1484                 Folder *folder;
1485                 gchar *tmp = NULL;
1486                 FILE *tmpfp;
1487
1488                 /* write to temporary file */
1489                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1490                             G_DIR_SEPARATOR, (gint)file);
1491                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1492                         FILE_OP_ERROR(tmp, "fopen");
1493                         newsval = -1;
1494                         alertpanel_error(_("Could not create temporary file for news sending."));
1495                 } else {
1496                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1497                                 FILE_OP_ERROR(tmp, "chmod");
1498                                 g_warning("can't change file mode\n");
1499                         }
1500
1501                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1502                                 if (fputs(buf, tmpfp) == EOF) {
1503                                         FILE_OP_ERROR(tmp, "fputs");
1504                                         newsval = -1;
1505                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1506                                 }
1507                         }
1508                         fclose(tmpfp);
1509
1510                         if (newsval == 0) {
1511                                 debug_print("Sending message by news\n");
1512
1513                                 folder = FOLDER(newsac->folder);
1514
1515                                 newsval = news_post(folder, tmp);
1516                                 if (newsval < 0) {
1517                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1518                                                  newsac->nntp_server);
1519                                 }
1520                         }
1521                         g_unlink(tmp);
1522                 }
1523                 g_free(tmp);
1524         }
1525
1526         fclose(fp);
1527
1528         /* save message to outbox */
1529         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1530                 FolderItem *outbox;
1531
1532                 debug_print("saving sent message...\n");
1533
1534                 outbox = folder_find_item_from_identifier(savecopyfolder);
1535                 if (!outbox)
1536                         outbox = folder_get_default_outbox();
1537                         
1538                 if (save_clear_text || tmp_enc_file == NULL) {
1539                         procmsg_save_to_outbox(outbox, file, TRUE);
1540                 } else {
1541                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1542                 }
1543         }
1544
1545         if (tmp_enc_file != NULL) {
1546                 g_unlink(tmp_enc_file);
1547                 free(tmp_enc_file);
1548                 tmp_enc_file = NULL;
1549         }
1550
1551         if (replymessageid != NULL || fwdmessageid != NULL) {
1552                 gchar **tokens;
1553                 FolderItem *item;
1554                 
1555                 if (replymessageid != NULL)
1556                         tokens = g_strsplit(replymessageid, "\x7f", 0);
1557                 else
1558                         tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1559                 item = folder_find_item_from_identifier(tokens[0]);
1560
1561                 /* check if queued message has valid folder and message id */
1562                 if (item != NULL && tokens[2] != NULL) {
1563                         MsgInfo *msginfo;
1564                         
1565                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1566                 
1567                         /* check if referring message exists and has a message id */
1568                         if ((msginfo != NULL) && 
1569                             (msginfo->msgid != NULL) &&
1570                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1571                                 procmsg_msginfo_free(msginfo);
1572                                 msginfo = NULL;
1573                         }
1574                         
1575                         if (msginfo == NULL) {
1576                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1577                         }
1578                         
1579                         if (msginfo != NULL) {
1580                                 if (replymessageid != NULL) {
1581                                         procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1582                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1583                                 }  else {
1584                                         procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1585                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1586                                 }
1587                                 procmsg_msginfo_free(msginfo);
1588                         }
1589                 }
1590                 g_strfreev(tokens);
1591         }
1592
1593         g_free(from);
1594         g_free(smtpserver);
1595         slist_free_strings(to_list);
1596         g_slist_free(to_list);
1597         slist_free_strings(newsgroup_list);
1598         g_slist_free(newsgroup_list);
1599         g_free(savecopyfolder);
1600         g_free(replymessageid);
1601         g_free(fwdmessageid);
1602         g_free(privacy_system);
1603         g_free(encrypt_data);
1604
1605         return (newsval != 0 ? newsval : mailval);
1606 }
1607
1608 gint procmsg_send_message_queue(const gchar *file)
1609 {
1610         return procmsg_send_message_queue_full(file, FALSE);
1611 }
1612
1613 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1614 {
1615         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1616
1617         /* NEW flag */
1618         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1619                 item->new_msgs++;
1620         }
1621
1622         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1623                 item->new_msgs--;
1624         }
1625
1626         /* UNREAD flag */
1627         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1628                 item->unread_msgs++;
1629                 if (procmsg_msg_has_marked_parent(msginfo))
1630                         item->unreadmarked_msgs++;
1631         }
1632
1633         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1634                 item->unread_msgs--;
1635                 if (procmsg_msg_has_marked_parent(msginfo))
1636                         item->unreadmarked_msgs--;
1637         }
1638         
1639         /* MARK flag */
1640         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1641                 procmsg_update_unread_children(msginfo, TRUE);
1642                 item->marked_msgs++;
1643         }
1644
1645         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1646                 procmsg_update_unread_children(msginfo, FALSE);
1647                 item->marked_msgs--;
1648         }
1649 }
1650
1651 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1652 {
1653         FolderItem *item;
1654         MsgInfoUpdate msginfo_update;
1655         MsgPermFlags perm_flags_new, perm_flags_old;
1656         MsgTmpFlags tmp_flags_old;
1657
1658         g_return_if_fail(msginfo != NULL);
1659         item = msginfo->folder;
1660         g_return_if_fail(item != NULL);
1661         
1662         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1663
1664         /* Perm Flags handling */
1665         perm_flags_old = msginfo->flags.perm_flags;
1666         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1667         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1668                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1669         }
1670
1671         if (perm_flags_old != perm_flags_new) {
1672                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1673
1674                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1675
1676         }
1677
1678         /* Tmp flags handling */
1679         tmp_flags_old = msginfo->flags.tmp_flags;
1680         msginfo->flags.tmp_flags |= tmp_flags;
1681
1682         /* update notification */
1683         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1684                 msginfo_update.msginfo = msginfo;
1685                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1686                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1687                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1688         }
1689 }
1690
1691 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1692 {
1693         FolderItem *item;
1694         MsgInfoUpdate msginfo_update;
1695         MsgPermFlags perm_flags_new, perm_flags_old;
1696         MsgTmpFlags tmp_flags_old;
1697
1698         g_return_if_fail(msginfo != NULL);
1699         item = msginfo->folder;
1700         g_return_if_fail(item != NULL);
1701         
1702         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1703
1704         /* Perm Flags handling */
1705         perm_flags_old = msginfo->flags.perm_flags;
1706         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1707         
1708         if (perm_flags_old != perm_flags_new) {
1709                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1710
1711                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1712
1713                 msginfo_update.msginfo = msginfo;
1714                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1715                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1716                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1717         }
1718
1719         /* Tmp flags hanlding */
1720         tmp_flags_old = msginfo->flags.tmp_flags;
1721         msginfo->flags.tmp_flags &= ~tmp_flags;
1722
1723         /* update notification */
1724         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1725                 msginfo_update.msginfo = msginfo;
1726                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1727                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1728                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1729         }
1730 }
1731
1732 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1733                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1734                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1735 {
1736         FolderItem *item;
1737         MsgInfoUpdate msginfo_update;
1738         MsgPermFlags perm_flags_new, perm_flags_old;
1739         MsgTmpFlags tmp_flags_old;
1740
1741         g_return_if_fail(msginfo != NULL);
1742         item = msginfo->folder;
1743         g_return_if_fail(item != NULL);
1744         
1745         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1746
1747         /* Perm Flags handling */
1748         perm_flags_old = msginfo->flags.perm_flags;
1749         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1750         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1751                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1752         }
1753
1754         if (perm_flags_old != perm_flags_new) {
1755                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1756
1757                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1758
1759         }
1760
1761         /* Tmp flags handling */
1762         tmp_flags_old = msginfo->flags.tmp_flags;
1763         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1764         msginfo->flags.tmp_flags |= add_tmp_flags;
1765
1766         /* update notification */
1767         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1768                 msginfo_update.msginfo = msginfo;
1769                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1770                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1771                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1772         }
1773 }
1774
1775 /*!
1776  *\brief        check for flags (e.g. mark) in prior msgs of current thread
1777  *
1778  *\param        info Current message
1779  *\param        perm_flags Flags to be checked
1780  *\param        parentmsgs Hash of prior msgs to avoid loops
1781  *
1782  *\return       gboolean TRUE if perm_flags are found
1783  */
1784 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1785                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1786 {
1787         MsgInfo *tmp;
1788
1789         g_return_val_if_fail(info != NULL, FALSE);
1790
1791         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1792                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1793                                 info->inreplyto);
1794                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1795                         procmsg_msginfo_free(tmp);
1796                         return TRUE;
1797                 } else if (tmp != NULL) {
1798                         gboolean result;
1799
1800                         if (g_hash_table_lookup(parentmsgs, info)) {
1801                                 debug_print("loop detected: %s%c%d\n",
1802                                         folder_item_get_path(info->folder),
1803                                         G_DIR_SEPARATOR, info->msgnum);
1804                                 result = FALSE;
1805                         } else {
1806                                 g_hash_table_insert(parentmsgs, info, "1");
1807                                 result = procmsg_msg_has_flagged_parent_real(
1808                                     tmp, perm_flags, parentmsgs);
1809                         }
1810                         procmsg_msginfo_free(tmp);
1811                         return result;
1812                 } else {
1813                         return FALSE;
1814                 }
1815         } else
1816                 return FALSE;
1817 }
1818
1819 /*!
1820  *\brief        Callback for cleaning up hash of parentmsgs
1821  */
1822 gboolean parentmsgs_hash_remove(gpointer key,
1823                             gpointer value,
1824                             gpointer user_data)
1825 {
1826         return TRUE;
1827 }
1828
1829 /*!
1830  *\brief        Set up list of parentmsgs
1831  *              See procmsg_msg_has_flagged_parent_real()
1832  */
1833 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1834 {
1835         gboolean result;
1836         GHashTable *parentmsgs = g_hash_table_new(NULL, NULL); 
1837
1838         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1839         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1840         g_hash_table_destroy(parentmsgs);
1841         return result;
1842 }
1843
1844 /*!
1845  *\brief        Check if msgs prior in thread are marked
1846  *              See procmsg_msg_has_flagged_parent_real()
1847  */
1848 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1849 {
1850         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1851 }
1852
1853
1854 GSList *procmsg_find_children_func(MsgInfo *info, 
1855                                    GSList *children, GSList *all)
1856 {
1857         GSList *cur;
1858
1859         g_return_val_if_fail(info!=NULL, children);
1860         if (info->msgid == NULL)
1861                 return children;
1862
1863         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1864                 MsgInfo *tmp = (MsgInfo *)cur->data;
1865                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1866                         /* Check if message is already in the list */
1867                         if ((children == NULL) || 
1868                             (g_slist_index(children, tmp) == -1)) {
1869                                 children = g_slist_prepend(children,
1870                                                 procmsg_msginfo_new_ref(tmp));
1871                                 children = procmsg_find_children_func(tmp, 
1872                                                         children, 
1873                                                         all);
1874                         }
1875                 }
1876         }
1877         return children;
1878 }
1879
1880 GSList *procmsg_find_children (MsgInfo *info)
1881 {
1882         GSList *children;
1883         GSList *all, *cur;
1884
1885         g_return_val_if_fail(info!=NULL, NULL);
1886         all = folder_item_get_msg_list(info->folder);
1887         children = procmsg_find_children_func(info, NULL, all);
1888         if (children != NULL) {
1889                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1890                         /* this will not free the used pointers
1891                            created with procmsg_msginfo_new_ref */
1892                         procmsg_msginfo_free((MsgInfo *)cur->data);
1893                 }
1894         }
1895         g_slist_free(all);
1896
1897         return children;
1898 }
1899
1900 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1901 {
1902         GSList *children = procmsg_find_children(info);
1903         GSList *cur;
1904         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1905                 MsgInfo *tmp = (MsgInfo *)cur->data;
1906                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1907                         if(newly_marked) 
1908                                 info->folder->unreadmarked_msgs++;
1909                         else
1910                                 info->folder->unreadmarked_msgs--;
1911                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1912                 }
1913                 procmsg_msginfo_free(tmp);
1914         }
1915         g_slist_free(children);
1916 }
1917
1918 /**
1919  * Set the destination folder for a copy or move operation
1920  *
1921  * \param msginfo The message which's destination folder is changed
1922  * \param to_folder The destination folder for the operation
1923  */
1924 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1925 {
1926         if(msginfo->to_folder != NULL) {
1927                 msginfo->to_folder->op_count--;
1928                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1929         }
1930         msginfo->to_folder = to_folder;
1931         if(to_folder != NULL) {
1932                 to_folder->op_count++;
1933                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1934         }
1935 }
1936
1937 /**
1938  * Apply filtering actions to the msginfo
1939  *
1940  * \param msginfo The MsgInfo describing the message that should be filtered
1941  * \return TRUE if the message was moved and MsgInfo is now invalid,
1942  *         FALSE otherwise
1943  */
1944 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1945 {
1946         MailFilteringData mail_filtering_data;
1947                         
1948         mail_filtering_data.msginfo = msginfo;                  
1949         if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1950                 return TRUE;
1951
1952         /* filter if enabled in prefs or move to inbox if not */
1953         if((filtering_rules != NULL) &&
1954            filter_message_by_msginfo(filtering_rules, msginfo))
1955                 return TRUE;
1956                 
1957         hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1958
1959         return FALSE;
1960 }
1961
1962 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1963 {
1964         MsgInfo *tmp_msginfo = NULL;
1965         MsgFlags flags = {0, 0};
1966         gchar *tmpfile = get_tmp_file();
1967         FILE *fp = g_fopen(tmpfile, "wb");
1968         
1969         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1970             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1971                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1972                 if (fp) 
1973                         fclose(fp);
1974                 g_free(tmpfile);
1975                 return NULL;
1976         }
1977         
1978         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1979                 fclose(fp);
1980                 fp = NULL;
1981                 tmp_msginfo = procheader_parse_file(
1982                         tmpfile, flags, 
1983                         TRUE, FALSE);
1984         }
1985         if (fp)
1986                 fclose(fp);
1987
1988         if (tmp_msginfo != NULL) {
1989                 tmp_msginfo->folder = src_msginfo->folder;
1990                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
1991         } else {
1992                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
1993         }
1994
1995         g_free(tmpfile);
1996
1997         return tmp_msginfo;
1998 }