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