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