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