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