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