3bb41c9e09fa8ae8cf8a6f558283b182ce7767e7
[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 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
792 {
793         gchar *folder_id;
794         const gchar *msgid;
795         gchar *id;
796
797         cm_return_val_if_fail(msginfo != NULL, NULL);
798         folder_id = folder_item_get_identifier(msginfo->folder);
799         msgid = msginfo->msgid;
800
801         id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
802
803         g_free(folder_id);
804
805         return id;
806 }
807
808 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
809 {
810         gchar *folder_id = g_strdup(id);
811         gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
812         const gchar *msgid;
813         FolderItem *item;
814         MsgInfo *msginfo;
815
816         if (separator == NULL) {
817                 g_free(folder_id);
818                 return NULL;
819         }
820
821         *separator = '\0';
822         msgid = separator + 1;
823
824         item = folder_find_item_from_identifier(folder_id);
825
826         if (item == NULL) {
827                 g_free(folder_id);
828                 return NULL;
829         }
830
831         msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
832         g_free(folder_id);
833
834         return msginfo;
835 }
836
837 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
838 {
839         GSList *result = NULL;
840         GSList *orig = NULL;
841         PrefsAccount *last_account = NULL;
842         MsgInfo *msg = NULL;
843         GSList *cur = NULL;
844         gboolean nothing_to_sort = TRUE;
845
846         if (!list)
847                 return NULL;
848
849         orig = g_slist_copy(list);
850         
851         msg = (MsgInfo *)orig->data;
852         
853         for (cur = orig; cur; cur = cur->next)
854                 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
855         
856         debug_print("\n");
857
858 parse_again:    
859         nothing_to_sort = TRUE;
860         cur = orig;
861         while (cur) {
862                 gchar *file = NULL;
863                 PrefsAccount *ac = NULL;
864                 msg = (MsgInfo *)cur->data;
865                 file = folder_item_fetch_msg(queue, msg->msgnum);
866                 ac = procmsg_get_account_from_file(file);
867                 g_free(file);
868
869                 if (last_account == NULL || (ac != NULL && ac == last_account)) {
870                         result = g_slist_append(result, msg);
871                         orig = g_slist_remove(orig, msg);
872                         last_account = ac;
873                         nothing_to_sort = FALSE;
874                         goto parse_again;
875                 }
876                 cur = cur->next;
877         }
878         
879         if (orig || g_slist_length(orig)) {
880                 if (!last_account && nothing_to_sort) {
881                         /* can't find an account for the rest of the list */
882                         cur = orig;
883                         while (cur) {
884                                 result = g_slist_append(result, cur->data);
885                                 cur = cur->next;
886                         }
887                 } else {
888                         last_account = NULL;
889                         goto parse_again;
890                 }
891         }
892         
893         g_slist_free(orig);
894         
895         for (cur = result; cur; cur = cur->next)
896                 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
897
898         debug_print("\n");
899
900         return result;
901 }
902
903 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
904 {
905         gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
906         PrefsAccount *ac = procmsg_get_account_from_file(file);
907         GSList *cur;
908         g_free(file);
909         for (cur = elem; cur; cur = cur->next) {
910                 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
911                 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
912                 
913                 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
914                         if (procmsg_get_account_from_file(file) == ac) {
915                                 g_free(file);
916                                 return FALSE;
917                         }
918                 }
919                 
920                 g_free(file);
921         }
922         return TRUE;
923 }
924
925 static gboolean send_queue_lock = FALSE;
926
927 gboolean procmsg_queue_lock(char **errstr)
928 {
929         if (send_queue_lock) {
930                 /* Avoid having to translate two similar strings */
931                 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
932                 if (errstr) {
933                         if (*errstr) g_free(*errstr);
934                         *errstr = g_strdup_printf(_("Already trying to send."));
935                 }
936                 return FALSE;
937         }
938         send_queue_lock = TRUE;
939         return TRUE;
940 }
941 void procmsg_queue_unlock(void)
942 {
943         send_queue_lock = FALSE;
944 }
945 /*!
946  *\brief        Send messages in queue
947  *
948  *\param        queue Queue folder to process
949  *\param        save_msgs Unused
950  *
951  *\return       Number of messages sent, negative if an error occurred
952  *              positive if no error occurred
953  */
954 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
955 {
956         gint sent = 0, err = 0;
957         GSList *list, *elem;
958         GSList *sorted_list = NULL;
959         GNode *node, *next;
960         
961         if (!procmsg_queue_lock(errstr)) {
962                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
963                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
964                 return -1;
965         }
966         inc_lock();
967         if (!queue)
968                 queue = folder_get_default_queue();
969         
970         if (queue == NULL) {
971                 procmsg_queue_unlock();
972                 inc_unlock();
973                 return -1;
974         }
975
976         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
977         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
978
979         folder_item_scan(queue);
980         list = folder_item_get_msg_list(queue);
981
982         /* sort the list per sender account; this helps reusing the same SMTP server */
983         sorted_list = procmsg_list_sort_by_account(queue, list);
984         
985         for (elem = sorted_list; elem != NULL; elem = elem->next) {
986                 gchar *file;
987                 MsgInfo *msginfo;
988                         
989                 msginfo = (MsgInfo *)(elem->data);
990                 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
991                         file = folder_item_fetch_msg(queue, msginfo->msgnum);
992                         if (file) {
993                                 gboolean queued_removed = FALSE;
994                                 if (procmsg_send_message_queue_full(file, 
995                                                 !procmsg_is_last_for_account(queue, msginfo, elem),
996                                                 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
997                                         g_warning("Sending queued message %d failed.\n", 
998                                                   msginfo->msgnum);
999                                         err++;
1000                                 } else {
1001                                         sent++; 
1002                                         if (!queued_removed)
1003                                                 folder_item_remove_msg(queue, msginfo->msgnum);
1004                                 }
1005                                 g_free(file);
1006                         }
1007                 }
1008                 /* FIXME: supposedly if only one message is locked, and queue
1009                  * is being flushed, the following free says something like 
1010                  * "freeing msg ## in folder (nil)". */
1011                 procmsg_msginfo_free(msginfo);
1012         }
1013
1014         g_slist_free(sorted_list);
1015         folder_item_scan(queue);
1016
1017         if (queue->node && queue->node->children) {
1018                 node = queue->node->children;
1019                 while (node != NULL) {
1020                         int res = 0;
1021                         next = node->next;
1022                         send_queue_lock = FALSE;
1023                         res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1024                         send_queue_lock = TRUE;
1025                         if (res < 0) 
1026                                 err = -res;
1027                         else
1028                                 sent += res;
1029                         node = next;
1030                 }
1031         }
1032         procmsg_queue_unlock();
1033         inc_unlock();
1034         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1035         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1036
1037         return (err != 0 ? -err : sent);
1038 }
1039
1040 gboolean procmsg_is_sending(void)
1041 {
1042         return send_queue_lock;
1043 }
1044
1045 /*!
1046  *\brief        Determine if a queue folder is empty
1047  *
1048  *\param        queue Queue folder to process
1049  *
1050  *\return       TRUE if the queue folder is empty, otherwise return FALSE
1051  */
1052 gboolean procmsg_queue_is_empty(FolderItem *queue)
1053 {
1054         GSList *list;
1055         gboolean res = FALSE;
1056         if (!queue)
1057                 queue = folder_get_default_queue();
1058         cm_return_val_if_fail(queue != NULL, TRUE);
1059
1060         folder_item_scan(queue);
1061         list = folder_item_get_msg_list(queue);
1062         res = (list == NULL);
1063         procmsg_msg_list_free(list);
1064
1065         if (res == TRUE) {
1066                 GNode *node, *next;
1067                 if (queue->node && queue->node->children) {
1068                         node = queue->node->children;
1069                         while (node != NULL) {
1070                                 next = node->next;
1071                                 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1072                                         return FALSE;
1073                                 node = next;
1074                         }
1075                 }
1076         }
1077         return res;
1078 }
1079
1080 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1081 {
1082         FILE *fp, *outfp;
1083         gchar buf[BUFFSIZE];
1084         
1085         if ((fp = g_fopen(in, "rb")) == NULL) {
1086                 FILE_OP_ERROR(in, "fopen");
1087                 return -1;
1088         }
1089         if ((outfp = g_fopen(out, "wb")) == NULL) {
1090                 FILE_OP_ERROR(out, "fopen");
1091                 fclose(fp);
1092                 return -1;
1093         }
1094         while (fgets(buf, sizeof(buf), fp) != NULL) {
1095                 /* new way */
1096                 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1097                         strlen("X-Claws-End-Special-Headers:"))) ||
1098                     (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1099                         strlen("X-Sylpheed-End-Special-Headers:"))))
1100                         break;
1101                 /* old way */
1102                 if (buf[0] == '\r' || buf[0] == '\n') break;
1103                 /* from other mailers */
1104                 if (!strncmp(buf, "Date: ", 6)
1105                 ||  !strncmp(buf, "To: ", 4)
1106                 ||  !strncmp(buf, "From: ", 6)
1107                 ||  !strncmp(buf, "Subject: ", 9)) {
1108                         rewind(fp);
1109                         break;
1110                 }
1111         }
1112         while (fgets(buf, sizeof(buf), fp) != NULL)
1113                 fputs(buf, outfp);
1114         fclose(outfp);
1115         fclose(fp);
1116         return 0;
1117 }
1118
1119 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1120                             gboolean is_queued)
1121 {
1122         gint num;
1123         MsgInfo *msginfo, *tmp_msginfo;
1124         MsgFlags flag = {0, 0};
1125
1126         debug_print("saving sent message...\n");
1127
1128         if (!outbox)
1129                 outbox = folder_get_default_outbox();
1130         cm_return_val_if_fail(outbox != NULL, -1);
1131
1132         /* remove queueing headers */
1133         if (is_queued) {
1134                 gchar tmp[MAXPATHLEN + 1];
1135
1136                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1137                            get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1138                 
1139                 if (procmsg_remove_special_headers(file, tmp) !=0)
1140                         return -1;
1141
1142                 folder_item_scan(outbox);
1143                 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1144                         g_warning("can't save message\n");
1145                         claws_unlink(tmp);
1146                         return -1;
1147                 }
1148         } else {
1149                 folder_item_scan(outbox);
1150                 if ((num = folder_item_add_msg
1151                         (outbox, file, &flag, FALSE)) < 0) {
1152                         g_warning("can't save message\n");
1153                         return -1;
1154                 }
1155         }
1156         msginfo = folder_item_get_msginfo(outbox, num);         /* refcnt++ */
1157         tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);   /* refcnt++ */ 
1158         if (msginfo != NULL) {
1159                 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1160                 procmsg_msginfo_free(msginfo);                  /* refcnt-- */
1161                 /* tmp_msginfo == msginfo */
1162                 if (tmp_msginfo && msginfo->extradata && 
1163                     (msginfo->extradata->dispositionnotificationto || 
1164                      msginfo->extradata->returnreceiptto)) {
1165                         procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 
1166                 }       
1167                 procmsg_msginfo_free(tmp_msginfo);              /* refcnt-- */
1168         }
1169
1170         return 0;
1171 }
1172
1173
1174 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1175 {
1176         static const gchar *def_cmd = "lpr %s";
1177         static guint id = 0;
1178         gchar *prtmp;
1179         FILE *tmpfp, *prfp;
1180         gchar buf[1024];
1181         gchar *p;
1182         int r;
1183
1184         cm_return_if_fail(msginfo);
1185
1186         if (procmime_msginfo_is_encrypted(msginfo))
1187                 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1188         else
1189                 tmpfp = procmime_get_first_text_content(msginfo);
1190         if (tmpfp == NULL) {
1191                 g_warning("Can't get text part\n");
1192                 return;
1193         }
1194
1195         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1196                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1197
1198         if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1199                 FILE_OP_ERROR(prtmp, "fopen");
1200                 g_free(prtmp);
1201                 fclose(tmpfp);
1202                 return;
1203         }
1204
1205         if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1206         if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1207         if (msginfo->to)   { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1208         if (msginfo->cc)   { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1209         if (msginfo->newsgroups) {
1210                 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1211         }
1212         if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1213         if (fputc('\n', prfp) == EOF) goto fpferr;
1214
1215         while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1216                 r = fputs(buf, prfp);
1217                 if (r == EOF) goto fpferr;
1218         }
1219
1220         fclose(prfp);
1221         fclose(tmpfp);
1222
1223         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1224             !strchr(p + 2, '%'))
1225                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1226         else {
1227                 if (cmdline)
1228                         g_warning("Print command-line is invalid: '%s'\n",
1229                                   cmdline);
1230                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1231         }
1232
1233         g_free(prtmp);
1234
1235         g_strchomp(buf);
1236         if (buf[strlen(buf) - 1] != '&')
1237                 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1238         if (system(buf) == -1)
1239                 g_warning("system(%s) failed.", buf);
1240         return;
1241 fpferr:
1242         FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1243         g_free(prtmp);
1244         fclose(tmpfp);
1245         fclose(prfp);
1246 }
1247
1248 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1249 {
1250         msginfo->refcnt++;
1251         
1252         return msginfo;
1253 }
1254
1255 MsgInfo *procmsg_msginfo_new(void)
1256 {
1257         MsgInfo *newmsginfo;
1258
1259         newmsginfo = g_new0(MsgInfo, 1);
1260         newmsginfo->refcnt = 1;
1261         
1262         return newmsginfo;
1263 }
1264
1265 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1266 {
1267         MsgInfoAvatar *newavatar;
1268
1269         if (avatar == NULL) return NULL;
1270
1271         newavatar = g_new0(MsgInfoAvatar, 1);
1272         newavatar->avatar_id = avatar->avatar_id;
1273         newavatar->avatar_src = g_strdup(avatar->avatar_src);
1274
1275         return newavatar;
1276 }
1277
1278 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1279 {
1280         if (avatar != NULL) {
1281                 if (avatar->avatar_src != NULL)
1282                         g_free(avatar->avatar_src);
1283                 g_free(avatar);
1284         }
1285 }
1286
1287 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1288 {
1289         MsgInfo *newmsginfo;
1290         GSList *refs;
1291
1292         if (msginfo == NULL) return NULL;
1293
1294         newmsginfo = g_new0(MsgInfo, 1);
1295
1296         newmsginfo->refcnt = 1;
1297
1298 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1299 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1300                         g_strdup(msginfo->mmb) : NULL
1301
1302         MEMBCOPY(msgnum);
1303         MEMBCOPY(size);
1304         MEMBCOPY(mtime);
1305         MEMBCOPY(date_t);
1306
1307         MEMBCOPY(flags);
1308
1309         MEMBDUP(fromname);
1310
1311         MEMBDUP(date);
1312         MEMBDUP(from);
1313         MEMBDUP(to);
1314         MEMBDUP(cc);
1315         MEMBDUP(newsgroups);
1316         MEMBDUP(subject);
1317         MEMBDUP(msgid);
1318         MEMBDUP(inreplyto);
1319         MEMBDUP(xref);
1320
1321         MEMBCOPY(folder);
1322         MEMBCOPY(to_folder);
1323
1324         if (msginfo->extradata) {
1325                 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1326                 if (msginfo->extradata->avatars) {
1327                         newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1328                                                                 (GCopyFunc) procmsg_msginfoavatar_copy);
1329                 }
1330                 MEMBDUP(extradata->dispositionnotificationto);
1331                 MEMBDUP(extradata->returnreceiptto);
1332                 MEMBDUP(extradata->partial_recv);
1333                 MEMBDUP(extradata->account_server);
1334                 MEMBDUP(extradata->account_login);
1335                 MEMBDUP(extradata->list_post);
1336                 MEMBDUP(extradata->list_subscribe);
1337                 MEMBDUP(extradata->list_unsubscribe);
1338                 MEMBDUP(extradata->list_help);
1339                 MEMBDUP(extradata->list_archive);
1340                 MEMBDUP(extradata->list_owner);
1341         }
1342
1343         refs = msginfo->references;
1344         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1345                 newmsginfo->references = g_slist_prepend
1346                         (newmsginfo->references, g_strdup(refs->data)); 
1347         }
1348         newmsginfo->references = g_slist_reverse(newmsginfo->references);
1349
1350         MEMBCOPY(score);
1351         MEMBDUP(plaintext_file);
1352
1353         return newmsginfo;
1354 }
1355
1356 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1357 {
1358         MsgInfo *full_msginfo;
1359
1360         if (msginfo == NULL) return NULL;
1361
1362         if (!file || !is_file_exist(file)) {
1363                 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1364                 return NULL;
1365         }
1366
1367         full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1368         if (!full_msginfo) return NULL;
1369
1370         msginfo->total_size = full_msginfo->total_size;
1371         msginfo->planned_download = full_msginfo->planned_download;
1372
1373         if (full_msginfo->extradata) {
1374                 if (!msginfo->extradata)
1375                         msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1376                 if (!msginfo->extradata->list_post)
1377                         msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1378                 if (!msginfo->extradata->list_subscribe)
1379                         msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1380                 if (!msginfo->extradata->list_unsubscribe)
1381                         msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1382                 if (!msginfo->extradata->list_help)
1383                         msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1384                 if (!msginfo->extradata->list_archive)
1385                         msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1386                 if (!msginfo->extradata->list_owner)
1387                         msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1388                 if (!msginfo->extradata->avatars)
1389                         msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1390                                                                         (GCopyFunc) procmsg_msginfoavatar_copy);
1391                 if (!msginfo->extradata->dispositionnotificationto)
1392                         msginfo->extradata->dispositionnotificationto = 
1393                                 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1394                 if (!msginfo->extradata->returnreceiptto)
1395                         msginfo->extradata->returnreceiptto = g_strdup
1396                                 (full_msginfo->extradata->returnreceiptto);
1397                 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1398                         msginfo->extradata->partial_recv = g_strdup
1399                                 (full_msginfo->extradata->partial_recv);
1400                 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1401                         msginfo->extradata->account_server = g_strdup
1402                                 (full_msginfo->extradata->account_server);
1403                 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1404                         msginfo->extradata->account_login = g_strdup
1405                                 (full_msginfo->extradata->account_login);
1406         }
1407         procmsg_msginfo_free(full_msginfo);
1408
1409         return procmsg_msginfo_new_ref(msginfo);
1410 }
1411
1412 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1413 {
1414         MsgInfo *full_msginfo;
1415         gchar *file;
1416
1417         if (msginfo == NULL) return NULL;
1418
1419         file = procmsg_get_message_file_path(msginfo);
1420         if (!file || !is_file_exist(file)) {
1421                 g_free(file);
1422                 file = procmsg_get_message_file(msginfo);
1423         }
1424         if (!file || !is_file_exist(file)) {
1425                 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1426                 return NULL;
1427         }
1428
1429         full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1430         g_free(file);
1431         return full_msginfo;
1432 }
1433
1434 void procmsg_msginfo_free(MsgInfo *msginfo)
1435 {
1436         if (msginfo == NULL) return;
1437
1438         msginfo->refcnt--;
1439         if (msginfo->refcnt > 0)
1440                 return;
1441
1442         if (msginfo->to_folder) {
1443                 msginfo->to_folder->op_count--;
1444                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1445         }
1446
1447         g_free(msginfo->fromspace);
1448
1449         g_free(msginfo->fromname);
1450
1451         g_free(msginfo->date);
1452         g_free(msginfo->from);
1453         g_free(msginfo->to);
1454         g_free(msginfo->cc);
1455         g_free(msginfo->newsgroups);
1456         g_free(msginfo->subject);
1457         g_free(msginfo->msgid);
1458         g_free(msginfo->inreplyto);
1459         g_free(msginfo->xref);
1460
1461         if (msginfo->extradata) {
1462                 if (msginfo->extradata->avatars) {
1463                         g_slist_foreach(msginfo->extradata->avatars,
1464                                         (GFunc)procmsg_msginfoavatar_free,
1465                                         NULL);
1466                         g_slist_free(msginfo->extradata->avatars);
1467                 }
1468                 g_free(msginfo->extradata->returnreceiptto);
1469                 g_free(msginfo->extradata->dispositionnotificationto);
1470                 g_free(msginfo->extradata->list_post);
1471                 g_free(msginfo->extradata->list_subscribe);
1472                 g_free(msginfo->extradata->list_unsubscribe);
1473                 g_free(msginfo->extradata->list_help);
1474                 g_free(msginfo->extradata->list_archive);
1475                 g_free(msginfo->extradata->list_owner);
1476                 g_free(msginfo->extradata->partial_recv);
1477                 g_free(msginfo->extradata->account_server);
1478                 g_free(msginfo->extradata->account_login);
1479                 g_free(msginfo->extradata);
1480         }
1481         slist_free_strings_full(msginfo->references);
1482         g_slist_free(msginfo->tags);
1483
1484         g_free(msginfo->plaintext_file);
1485
1486         g_free(msginfo);
1487 }
1488
1489 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1490 {
1491         guint memusage = 0;
1492         GSList *tmp;
1493         
1494         memusage += sizeof(MsgInfo);
1495         if (msginfo->fromname)
1496                 memusage += strlen(msginfo->fromname);
1497         if (msginfo->date)
1498                 memusage += strlen(msginfo->date);
1499         if (msginfo->from)
1500                 memusage += strlen(msginfo->from);
1501         if (msginfo->to)
1502                 memusage += strlen(msginfo->to);
1503         if (msginfo->cc)
1504                 memusage += strlen(msginfo->cc);
1505         if (msginfo->newsgroups)
1506                 memusage += strlen(msginfo->newsgroups);
1507         if (msginfo->subject)
1508                 memusage += strlen(msginfo->subject);
1509         if (msginfo->msgid)
1510                 memusage += strlen(msginfo->msgid);
1511         if (msginfo->inreplyto)
1512                 memusage += strlen(msginfo->inreplyto);
1513
1514         for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1515                 gchar *r = (gchar *)tmp->data;
1516                 memusage += r?strlen(r):0 + sizeof(GSList);
1517         }
1518         if (msginfo->fromspace)
1519                 memusage += strlen(msginfo->fromspace);
1520
1521         for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1522                 memusage += sizeof(GSList);
1523         }
1524         if (msginfo->extradata) {
1525                 memusage += sizeof(MsgInfoExtraData);
1526                 if (msginfo->extradata->avatars) {
1527                         for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1528                                 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1529                                 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1530                                 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1531                         }
1532                 }
1533                 if (msginfo->extradata->dispositionnotificationto)
1534                         memusage += strlen(msginfo->extradata->dispositionnotificationto);
1535                 if (msginfo->extradata->returnreceiptto)
1536                         memusage += strlen(msginfo->extradata->returnreceiptto);
1537
1538                 if (msginfo->extradata->partial_recv)
1539                         memusage += strlen(msginfo->extradata->partial_recv);
1540                 if (msginfo->extradata->account_server)
1541                         memusage += strlen(msginfo->extradata->account_server);
1542                 if (msginfo->extradata->account_login)
1543                         memusage += strlen(msginfo->extradata->account_login);
1544
1545                 if (msginfo->extradata->list_post)
1546                         memusage += strlen(msginfo->extradata->list_post);
1547                 if (msginfo->extradata->list_subscribe)
1548                         memusage += strlen(msginfo->extradata->list_subscribe);
1549                 if (msginfo->extradata->list_unsubscribe)
1550                         memusage += strlen(msginfo->extradata->list_unsubscribe);
1551                 if (msginfo->extradata->list_help)
1552                         memusage += strlen(msginfo->extradata->list_help);
1553                 if (msginfo->extradata->list_archive)
1554                         memusage += strlen(msginfo->extradata->list_archive);
1555                 if (msginfo->extradata->list_owner)
1556                         memusage += strlen(msginfo->extradata->list_owner);
1557         }
1558         return memusage;
1559 }
1560
1561 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1562                                             FolderItem *queue, gint msgnum, gboolean *queued_removed)
1563 {
1564         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1565                                        {"SSV:",  NULL, FALSE},
1566                                        {"R:",    NULL, FALSE},
1567                                        {"NG:",   NULL, FALSE},
1568                                        {"MAID:", NULL, FALSE},
1569                                        {"NAID:", NULL, FALSE},
1570                                        {"SCF:",  NULL, FALSE},
1571                                        {"RMID:", NULL, FALSE},
1572                                        {"FMID:", NULL, FALSE},
1573                                        {"X-Claws-Privacy-System:", NULL, FALSE},
1574                                        {"X-Claws-Encrypt:", NULL, FALSE},
1575                                        {"X-Claws-Encrypt-Data:", NULL, FALSE},
1576                                        {"X-Claws-End-Special-Headers:", NULL, FALSE},
1577                                        {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1578                                        {"X-Sylpheed-Encrypt:", NULL, FALSE},
1579                                        {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1580                                        {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1581                                        {NULL,    NULL, FALSE}};
1582         FILE *fp;
1583         gint filepos;
1584         gint mailval = 0, newsval = 0;
1585         gchar *from = NULL;
1586         gchar *smtpserver = NULL;
1587         GSList *to_list = NULL;
1588         GSList *newsgroup_list = NULL;
1589         gchar *savecopyfolder = NULL;
1590         gchar *replymessageid = NULL;
1591         gchar *fwdmessageid = NULL;
1592         gchar *privacy_system = NULL;
1593         gboolean encrypt = FALSE;
1594         gchar *encrypt_data = NULL;
1595         gchar buf[BUFFSIZE];
1596         gint hnum;
1597         PrefsAccount *mailac = NULL, *newsac = NULL;
1598         gboolean save_clear_text = TRUE;
1599         gchar *tmp_enc_file = NULL;
1600
1601         cm_return_val_if_fail(file != NULL, -1);
1602
1603         if ((fp = g_fopen(file, "rb")) == NULL) {
1604                 FILE_OP_ERROR(file, "fopen");
1605                 if (errstr) {
1606                         if (*errstr) g_free(*errstr);
1607                         *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1608                 }
1609                 return -1;
1610         }
1611
1612         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1613                != -1) {
1614                 gchar *p = buf + strlen(qentry[hnum].name);
1615
1616                 switch (hnum) {
1617                 case Q_SENDER:
1618                         if (from == NULL) 
1619                                 from = g_strdup(p);
1620                         break;
1621                 case Q_SMTPSERVER:
1622                         if (smtpserver == NULL) 
1623                                 smtpserver = g_strdup(p);
1624                         break;
1625                 case Q_RECIPIENTS:
1626                         to_list = address_list_append(to_list, p);
1627                         break;
1628                 case Q_NEWSGROUPS:
1629                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1630                         break;
1631                 case Q_MAIL_ACCOUNT_ID:
1632                         mailac = account_find_from_id(atoi(p));
1633                         break;
1634                 case Q_NEWS_ACCOUNT_ID:
1635                         newsac = account_find_from_id(atoi(p));
1636                         break;
1637                 case Q_SAVE_COPY_FOLDER:
1638                         if (savecopyfolder == NULL) 
1639                                 savecopyfolder = g_strdup(p);
1640                         break;
1641                 case Q_REPLY_MESSAGE_ID:
1642                         if (replymessageid == NULL) 
1643                                 replymessageid = g_strdup(p);
1644                         break;
1645                 case Q_FWD_MESSAGE_ID:
1646                         if (fwdmessageid == NULL) 
1647                                 fwdmessageid = g_strdup(p);
1648                         break;
1649                 case Q_PRIVACY_SYSTEM:
1650                 case Q_PRIVACY_SYSTEM_OLD:
1651                         if (privacy_system == NULL) 
1652                                 privacy_system = g_strdup(p);
1653                         break;
1654                 case Q_ENCRYPT:
1655                 case Q_ENCRYPT_OLD:
1656                         if (p[0] == '1') 
1657                                 encrypt = TRUE;
1658                         break;
1659                 case Q_ENCRYPT_DATA:
1660                 case Q_ENCRYPT_DATA_OLD:
1661                         if (encrypt_data == NULL) 
1662                                 encrypt_data = g_strdup(p);
1663                         break;
1664                 case Q_CLAWS_HDRS:
1665                 case Q_CLAWS_HDRS_OLD:
1666                         /* end of special headers reached */
1667                         goto send_mail; /* can't "break;break;" */
1668                 }
1669         }
1670 send_mail:
1671         filepos = ftell(fp);
1672
1673         if (encrypt) {
1674                 MimeInfo *mimeinfo;
1675
1676                 if (mailac && mailac->save_encrypted_as_clear_text 
1677                 &&  !mailac->encrypt_to_self)
1678                         save_clear_text = TRUE;
1679                 else
1680                         save_clear_text = FALSE;
1681
1682                 fclose(fp);
1683                 fp = NULL;
1684
1685                 mimeinfo = procmime_scan_queue_file(file);
1686                 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1687                 || (fp = my_tmpfile()) == NULL
1688                 ||  procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1689                         if (fp)
1690                                 fclose(fp);
1691                         procmime_mimeinfo_free_all(mimeinfo);
1692                         g_free(from);
1693                         g_free(smtpserver);
1694                         slist_free_strings_full(to_list);
1695                         slist_free_strings_full(newsgroup_list);
1696                         g_free(savecopyfolder);
1697                         g_free(replymessageid);
1698                         g_free(fwdmessageid);
1699                         g_free(privacy_system);
1700                         g_free(encrypt_data);
1701                         if (errstr) {
1702                                 if (*errstr) g_free(*errstr);
1703                                 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1704                                                 privacy_get_error());
1705                         }
1706                         return -1;
1707                 }
1708                 
1709                 rewind(fp);
1710                 if (!save_clear_text) {
1711                         gchar *content = NULL;
1712                         FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1713                         if (tmpfp) {
1714                                 fclose(tmpfp);
1715
1716                                 content = file_read_stream_to_str(fp);
1717                                 rewind(fp);
1718
1719                                 str_write_to_file(content, tmp_enc_file);
1720                                 g_free(content);
1721                         } else {
1722                                 g_warning("couldn't get tempfile\n");
1723                         }
1724                 } 
1725                 
1726                 procmime_mimeinfo_free_all(mimeinfo);
1727                 
1728                 filepos = 0;
1729         }
1730
1731         if (to_list) {
1732                 debug_print("Sending message by mail\n");
1733                 if (!from) {
1734                         if (errstr) {
1735                                 if (*errstr) g_free(*errstr);
1736                                 *errstr = g_strdup_printf(_("Queued message header is broken."));
1737                         }
1738                         mailval = -1;
1739                 } else if (mailac && mailac->use_mail_command &&
1740                            mailac->mail_command && (* mailac->mail_command)) {
1741                         mailval = send_message_local(mailac->mail_command, fp);
1742                 } else {
1743                         if (!mailac) {
1744                                 mailac = account_find_from_smtp_server(from, smtpserver);
1745                                 if (!mailac) {
1746                                         g_warning("Account not found. "
1747                                                     "Using current account...\n");
1748                                         mailac = cur_account;
1749                                 }
1750                         }
1751
1752                         if (mailac) {
1753                                 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1754                                 if (mailval == -1 && errstr) {
1755                                         if (*errstr) g_free(*errstr);
1756                                         *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1757                                 }
1758                         } else {
1759                                 PrefsAccount tmp_ac;
1760
1761                                 g_warning("Account not found.\n");
1762
1763                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1764                                 tmp_ac.address = from;
1765                                 tmp_ac.smtp_server = smtpserver;
1766                                 tmp_ac.smtpport = SMTP_PORT;
1767                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1768                                 if (mailval == -1 && errstr) {
1769                                         if (*errstr) g_free(*errstr);
1770                                         *errstr = g_strdup_printf(_("No specific account has been found to "
1771                                                         "send, and an error happened during SMTP session."));
1772                                 }
1773                         }
1774                 }
1775         } else if (!to_list && !newsgroup_list) {
1776                 if (errstr) {
1777                         if (*errstr) g_free(*errstr);
1778                         *errstr = g_strdup(_("Couldn't determine sending information. "
1779                                 "Maybe the email hasn't been generated by Claws Mail."));
1780                 }
1781                 mailval = -1;
1782         }
1783
1784         fseek(fp, filepos, SEEK_SET);
1785         if (newsgroup_list && newsac && (mailval == 0)) {
1786                 Folder *folder;
1787                 gchar *tmp = NULL;
1788                 FILE *tmpfp;
1789
1790                 /* write to temporary file */
1791                 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1792                             G_DIR_SEPARATOR, file);
1793                 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1794                         FILE_OP_ERROR(tmp, "fopen");
1795                         newsval = -1;
1796                         alertpanel_error(_("Couldn't create temporary file for news sending."));
1797                 } else {
1798                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1799                                 FILE_OP_ERROR(tmp, "chmod");
1800                                 g_warning("can't change file mode\n");
1801                         }
1802
1803                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1804                                 if (fputs(buf, tmpfp) == EOF) {
1805                                         FILE_OP_ERROR(tmp, "fputs");
1806                                         newsval = -1;
1807                                         if (errstr) {
1808                                                 if (*errstr) g_free(*errstr);
1809                                                 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1810                                         }
1811                                 }
1812                         }
1813                         fclose(tmpfp);
1814
1815                         if (newsval == 0) {
1816                                 debug_print("Sending message by news\n");
1817
1818                                 folder = FOLDER(newsac->folder);
1819
1820                                 newsval = news_post(folder, tmp);
1821                                 if (newsval < 0 && errstr)  {
1822                                         if (*errstr) g_free(*errstr);
1823                                         *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1824                                          newsac->nntp_server);
1825                                 }
1826                         }
1827                         claws_unlink(tmp);
1828                 }
1829                 g_free(tmp);
1830         }
1831
1832         fclose(fp);
1833
1834         /* update session statistics */
1835         if (mailval == 0 && newsval == 0) {
1836                 /* update session stats */
1837                 if (replymessageid)
1838                         session_stats.replied++;
1839                 else if (fwdmessageid)
1840                         session_stats.forwarded++;
1841                 else
1842                         session_stats.sent++;
1843         }
1844
1845         /* save message to outbox */
1846         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1847                 FolderItem *outbox;
1848
1849                 debug_print("saving sent message...\n");
1850
1851                 outbox = folder_find_item_from_identifier(savecopyfolder);
1852                 if (!outbox)
1853                         outbox = folder_get_default_outbox();
1854                         
1855                 if (save_clear_text || tmp_enc_file == NULL) {
1856                         gboolean saved = FALSE;
1857                         *queued_removed = FALSE;
1858                         if (queue && msgnum > 0) {
1859                                 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1860                                 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1861                                         debug_print("moved queued mail %d to sent folder\n", msgnum);
1862                                         saved = TRUE;
1863                                         *queued_removed = TRUE;
1864                                 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1865                                         debug_print("copied queued mail %d to sent folder\n", msgnum);
1866                                         saved = TRUE;
1867                                 }
1868                                 procmsg_msginfo_free(queued_mail);
1869                         }
1870                         if (!saved) {
1871                                 debug_print("resaving clear text queued mail to sent folder\n");
1872                                 procmsg_save_to_outbox(outbox, file, TRUE);
1873                         }
1874                 } else {
1875                         debug_print("saving encrpyted queued mail to sent folder\n");
1876                         procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1877                 }
1878         }
1879
1880         if (tmp_enc_file != NULL) {
1881                 claws_unlink(tmp_enc_file);
1882                 free(tmp_enc_file);
1883                 tmp_enc_file = NULL;
1884         }
1885
1886         if (replymessageid != NULL || fwdmessageid != NULL) {
1887                 gchar **tokens;
1888                 FolderItem *item;
1889                 
1890                 if (replymessageid != NULL)
1891                         tokens = g_strsplit(replymessageid, "\t", 0);
1892                 else
1893                         tokens = g_strsplit(fwdmessageid, "\t", 0);
1894                 item = folder_find_item_from_identifier(tokens[0]);
1895
1896                 /* check if queued message has valid folder and message id */
1897                 if (item != NULL && tokens[2] != NULL) {
1898                         MsgInfo *msginfo;
1899                         
1900                         msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1901                 
1902                         /* check if referring message exists and has a message id */
1903                         if ((msginfo != NULL) && 
1904                             (msginfo->msgid != NULL) &&
1905                             (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1906                                 procmsg_msginfo_free(msginfo);
1907                                 msginfo = NULL;
1908                         }
1909                         
1910                         if (msginfo == NULL) {
1911                                 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1912                         }
1913                         
1914                         if (msginfo != NULL) {
1915                                 if (replymessageid != NULL) {
1916                                         MsgPermFlags to_unset = 0;
1917
1918                                         if (prefs_common.mark_as_read_on_new_window)
1919                                                 to_unset = (MSG_NEW|MSG_UNREAD);
1920
1921                                         procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1922                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1923                                 }  else {
1924                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1925                                 }
1926                                 procmsg_msginfo_free(msginfo);
1927                         }
1928                 }
1929                 g_strfreev(tokens);
1930         }
1931
1932         g_free(from);
1933         g_free(smtpserver);
1934         slist_free_strings_full(to_list);
1935         slist_free_strings_full(newsgroup_list);
1936         g_free(savecopyfolder);
1937         g_free(replymessageid);
1938         g_free(fwdmessageid);
1939         g_free(privacy_system);
1940         g_free(encrypt_data);
1941
1942         return (newsval != 0 ? newsval : mailval);
1943 }
1944
1945 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1946 {
1947         gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1948         main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1949         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1950         return result;
1951 }
1952
1953 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1954 {
1955         gint val;
1956         if (procmsg_queue_lock(errstr)) {
1957                 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1958                 procmsg_queue_unlock();
1959                 return val;
1960         }
1961         return -1;
1962 }
1963
1964 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1965 {
1966         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1967
1968         /* NEW flag */
1969         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1970                 item->new_msgs++;
1971         }
1972
1973         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1974                 item->new_msgs--;
1975         }
1976
1977         /* UNREAD flag */
1978         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1979                 item->unread_msgs++;
1980                 if (procmsg_msg_has_marked_parent(msginfo))
1981                         item->unreadmarked_msgs++;
1982         }
1983
1984         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1985                 item->unread_msgs--;
1986                 if (procmsg_msg_has_marked_parent(msginfo))
1987                         item->unreadmarked_msgs--;
1988         }
1989         
1990         /* MARK flag */
1991         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1992                 procmsg_update_unread_children(msginfo, TRUE);
1993                 item->marked_msgs++;
1994         }
1995
1996         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1997                 procmsg_update_unread_children(msginfo, FALSE);
1998                 item->marked_msgs--;
1999         }
2000
2001         if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
2002                 item->replied_msgs++;
2003         }
2004
2005         if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
2006                 item->replied_msgs--;
2007         }
2008
2009         if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
2010                 item->forwarded_msgs++;
2011         }
2012
2013         if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
2014                 item->forwarded_msgs--;
2015         }
2016
2017         if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
2018                 item->locked_msgs++;
2019         }
2020
2021         if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
2022                 item->locked_msgs--;
2023         }
2024
2025         if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
2026                 item->ignored_msgs--;
2027         }
2028
2029         if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
2030                 item->ignored_msgs++;
2031         }
2032
2033         if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
2034                 item->watched_msgs--;
2035         }
2036
2037         if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2038                 item->watched_msgs++;
2039         }
2040 }
2041
2042 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2043 {
2044         FolderItem *item;
2045         MsgInfoUpdate msginfo_update;
2046         MsgPermFlags perm_flags_new, perm_flags_old;
2047         MsgTmpFlags tmp_flags_old;
2048
2049         cm_return_if_fail(msginfo != NULL);
2050         item = msginfo->folder;
2051         cm_return_if_fail(item != NULL);
2052         
2053         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2054
2055         /* Perm Flags handling */
2056         perm_flags_old = msginfo->flags.perm_flags;
2057         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2058         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2059                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2060         }
2061         if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2062                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2063         }
2064
2065         if (perm_flags_old != perm_flags_new) {
2066                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2067
2068                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2069                 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2070         }
2071
2072         /* Tmp flags handling */
2073         tmp_flags_old = msginfo->flags.tmp_flags;
2074         msginfo->flags.tmp_flags |= tmp_flags;
2075
2076         /* update notification */
2077         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2078                 msginfo_update.msginfo = msginfo;
2079                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2080                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2081                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2082         }
2083 }
2084
2085 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2086 {
2087         FolderItem *item;
2088         MsgInfoUpdate msginfo_update;
2089         MsgPermFlags perm_flags_new, perm_flags_old;
2090         MsgTmpFlags tmp_flags_old;
2091
2092         cm_return_if_fail(msginfo != NULL);
2093         item = msginfo->folder;
2094         cm_return_if_fail(item != NULL);
2095         
2096         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2097
2098         /* Perm Flags handling */
2099         perm_flags_old = msginfo->flags.perm_flags;
2100         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2101         
2102         if (perm_flags_old != perm_flags_new) {
2103                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2104
2105                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2106         }
2107
2108         /* Tmp flags hanlding */
2109         tmp_flags_old = msginfo->flags.tmp_flags;
2110         msginfo->flags.tmp_flags &= ~tmp_flags;
2111
2112         /* update notification */
2113         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2114                 msginfo_update.msginfo = msginfo;
2115                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2116                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2117                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2118         }
2119 }
2120
2121 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
2122                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2123                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2124 {
2125         FolderItem *item;
2126         MsgInfoUpdate msginfo_update;
2127         MsgPermFlags perm_flags_new, perm_flags_old;
2128         MsgTmpFlags tmp_flags_old;
2129
2130         cm_return_if_fail(msginfo != NULL);
2131         item = msginfo->folder;
2132         cm_return_if_fail(item != NULL);
2133         
2134         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2135
2136         /* Perm Flags handling */
2137         perm_flags_old = msginfo->flags.perm_flags;
2138         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2139         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2140                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2141         }
2142         if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2143                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2144         }
2145
2146         if (perm_flags_old != perm_flags_new) {
2147                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2148
2149                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2150
2151         }
2152
2153         /* Tmp flags handling */
2154         tmp_flags_old = msginfo->flags.tmp_flags;
2155         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2156         msginfo->flags.tmp_flags |= add_tmp_flags;
2157
2158         /* update notification */
2159         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2160                 msginfo_update.msginfo = msginfo;
2161                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2162                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2163                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2164         }
2165 }
2166
2167 /*!
2168  *\brief        check for flags (e.g. mark) in prior msgs of current thread
2169  *
2170  *\param        info Current message
2171  *\param        perm_flags Flags to be checked
2172  *\param        parentmsgs Hash of prior msgs to avoid loops
2173  *
2174  *\return       gboolean TRUE if perm_flags are found
2175  */
2176 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2177                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2178 {
2179         MsgInfo *tmp;
2180
2181         cm_return_val_if_fail(info != NULL, FALSE);
2182
2183         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2184                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2185                                 info->inreplyto);
2186                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2187                         procmsg_msginfo_free(tmp);
2188                         return TRUE;
2189                 } else if (tmp != NULL) {
2190                         gboolean result;
2191
2192                         if (g_hash_table_lookup(parentmsgs, info)) {
2193                                 debug_print("loop detected: %d\n",
2194                                         info->msgnum);
2195                                 result = FALSE;
2196                         } else {
2197                                 g_hash_table_insert(parentmsgs, info, "1");
2198                                 result = procmsg_msg_has_flagged_parent_real(
2199                                     tmp, perm_flags, parentmsgs);
2200                         }
2201                         procmsg_msginfo_free(tmp);
2202                         return result;
2203                 } else {
2204                         return FALSE;
2205                 }
2206         } else
2207                 return FALSE;
2208 }
2209
2210 /*!
2211  *\brief        Callback for cleaning up hash of parentmsgs
2212  */
2213 static gboolean parentmsgs_hash_remove(gpointer key,
2214                             gpointer value,
2215                             gpointer user_data)
2216 {
2217         return TRUE;
2218 }
2219
2220 /*!
2221  *\brief        Set up list of parentmsgs
2222  *              See procmsg_msg_has_flagged_parent_real()
2223  */
2224 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2225 {
2226         gboolean result;
2227         static GHashTable *parentmsgs = NULL;
2228         
2229         if (parentmsgs == NULL)
2230                 parentmsgs = g_hash_table_new(NULL, NULL); 
2231
2232         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2233         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2234
2235         return result;
2236 }
2237
2238 /*!
2239  *\brief        Check if msgs prior in thread are marked
2240  *              See procmsg_msg_has_flagged_parent_real()
2241  */
2242 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2243 {
2244         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2245 }
2246
2247
2248 static GSList *procmsg_find_children_func(MsgInfo *info, 
2249                                    GSList *children, GSList *all)
2250 {
2251         GSList *cur;
2252
2253         cm_return_val_if_fail(info!=NULL, children);
2254         if (info->msgid == NULL)
2255                 return children;
2256
2257         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2258                 MsgInfo *tmp = (MsgInfo *)cur->data;
2259                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2260                         /* Check if message is already in the list */
2261                         if ((children == NULL) || 
2262                             (g_slist_index(children, tmp) == -1)) {
2263                                 children = g_slist_prepend(children,
2264                                                 procmsg_msginfo_new_ref(tmp));
2265                                 children = procmsg_find_children_func(tmp, 
2266                                                         children, 
2267                                                         all);
2268                         }
2269                 }
2270         }
2271         return children;
2272 }
2273
2274 static GSList *procmsg_find_children (MsgInfo *info)
2275 {
2276         GSList *children;
2277         GSList *all, *cur;
2278
2279         cm_return_val_if_fail(info!=NULL, NULL);
2280         all = folder_item_get_msg_list(info->folder);
2281         children = procmsg_find_children_func(info, NULL, all);
2282         if (children != NULL) {
2283                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2284                         /* this will not free the used pointers
2285                            created with procmsg_msginfo_new_ref */
2286                         procmsg_msginfo_free((MsgInfo *)cur->data);
2287                 }
2288         }
2289         g_slist_free(all);
2290
2291         return children;
2292 }
2293
2294 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2295 {
2296         GSList *children = procmsg_find_children(info);
2297         GSList *cur;
2298         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2299                 MsgInfo *tmp = (MsgInfo *)cur->data;
2300                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2301                         if(newly_marked) 
2302                                 info->folder->unreadmarked_msgs++;
2303                         else
2304                                 info->folder->unreadmarked_msgs--;
2305                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2306                 }
2307                 procmsg_msginfo_free(tmp);
2308         }
2309         g_slist_free(children);
2310 }
2311
2312 /**
2313  * Set the destination folder for a copy or move operation
2314  *
2315  * \param msginfo The message which's destination folder is changed
2316  * \param to_folder The destination folder for the operation
2317  */
2318 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2319 {
2320         if(msginfo->to_folder != NULL) {
2321                 msginfo->to_folder->op_count--;
2322                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2323         }
2324         msginfo->to_folder = to_folder;
2325         if(to_folder != NULL) {
2326                 to_folder->op_count++;
2327                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2328         }
2329 }
2330
2331 /**
2332  * Apply filtering actions to the msginfo
2333  *
2334  * \param msginfo The MsgInfo describing the message that should be filtered
2335  * \return TRUE if the message was moved and MsgInfo is now invalid,
2336  *         FALSE otherwise
2337  */
2338 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2339 {
2340         MailFilteringData mail_filtering_data;
2341                         
2342         mail_filtering_data.msginfo = msginfo;                  
2343         mail_filtering_data.msglist = NULL;                     
2344         mail_filtering_data.filtered = NULL;                    
2345         mail_filtering_data.unfiltered = NULL;
2346         mail_filtering_data.account = ac_prefs; 
2347
2348         if (!ac_prefs || ac_prefs->filterhook_on_recv)
2349                 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2350                 return TRUE;
2351
2352         /* filter if enabled in prefs or move to inbox if not */
2353         if((filtering_rules != NULL) &&
2354                 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2355                                 FILTERING_INCORPORATION, NULL)) {
2356                 return TRUE;
2357         }
2358                 
2359         return FALSE;
2360 }
2361
2362 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac, 
2363                             GSList **filtered, GSList **unfiltered,
2364                             gboolean do_filter)
2365 {
2366         GSList *cur, *to_do = NULL;
2367         gint total = 0, curnum = 0;
2368         MailFilteringData mail_filtering_data;
2369                         
2370         cm_return_if_fail(filtered != NULL);
2371         cm_return_if_fail(unfiltered != NULL);
2372
2373         *filtered = NULL;
2374         *unfiltered = NULL;
2375         
2376         if (list == NULL)
2377                 return;
2378
2379         total = g_slist_length(list);
2380
2381         if (!do_filter) {
2382                 *filtered = NULL;
2383                 *unfiltered = g_slist_copy(list);
2384                 return;
2385         }
2386
2387         statusbar_print_all(_("Filtering messages...\n"));
2388
2389         mail_filtering_data.msginfo = NULL;                     
2390         mail_filtering_data.msglist = list;                     
2391         mail_filtering_data.filtered = NULL;                    
2392         mail_filtering_data.unfiltered = NULL;  
2393         mail_filtering_data.account = ac;       
2394                         
2395         if (!ac || ac->filterhook_on_recv)
2396         hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2397         
2398         if (mail_filtering_data.filtered == NULL &&
2399             mail_filtering_data.unfiltered == NULL) {
2400                 /* nothing happened */
2401                 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2402                 to_do = list;
2403         } 
2404         if (mail_filtering_data.filtered != NULL) {
2405                 /* keep track of what's been filtered by the hooks */
2406                 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2407                         g_slist_length(list),
2408                         g_slist_length(mail_filtering_data.filtered),
2409                         g_slist_length(mail_filtering_data.unfiltered));
2410
2411                 *filtered = g_slist_copy(mail_filtering_data.filtered);
2412         }
2413         if (mail_filtering_data.unfiltered != NULL) {
2414                 /* what the hooks didn't handle will go in filtered or 
2415                  * unfiltered in the next loop */
2416                 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2417                         g_slist_length(list),
2418                         g_slist_length(mail_filtering_data.filtered),
2419                         g_slist_length(mail_filtering_data.unfiltered));
2420                 to_do = mail_filtering_data.unfiltered;
2421         } 
2422
2423         for (cur = to_do; cur; cur = cur->next) {
2424                 MsgInfo *info = (MsgInfo *)cur->data;
2425                 if (procmsg_msginfo_filter(info, ac))
2426                         *filtered = g_slist_prepend(*filtered, info);
2427                 else
2428                         *unfiltered = g_slist_prepend(*unfiltered, info);
2429                 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2430         }
2431
2432         g_slist_free(mail_filtering_data.filtered);
2433         g_slist_free(mail_filtering_data.unfiltered);
2434         
2435         *filtered = g_slist_reverse(*filtered);
2436         *unfiltered = g_slist_reverse(*unfiltered);
2437
2438         statusbar_progress_all(0,0,0);
2439         statusbar_pop_all();
2440 }
2441
2442 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2443 {
2444         MsgInfo *tmp_msginfo = NULL;
2445         MsgFlags flags = {0, 0};
2446         gchar *tmpfile = get_tmp_file();
2447         FILE *fp = g_fopen(tmpfile, "wb");
2448         
2449         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2450             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2451                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2452                 if (fp) 
2453                         fclose(fp);
2454                 g_free(tmpfile);
2455                 return NULL;
2456         }
2457         
2458         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2459                 fclose(fp);
2460                 fp = NULL;
2461                 tmp_msginfo = procheader_parse_file(
2462                         tmpfile, flags, 
2463                         TRUE, FALSE);
2464         }
2465         if (fp)
2466                 fclose(fp);
2467
2468         if (tmp_msginfo != NULL) {
2469                 if (src_msginfo)
2470                         tmp_msginfo->folder = src_msginfo->folder;
2471                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2472         } else {
2473                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2474         }
2475
2476         g_free(tmpfile);
2477
2478         return tmp_msginfo;
2479 }
2480
2481 static GSList *spam_learners = NULL;
2482
2483 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2484 {
2485         if (!g_slist_find(spam_learners, learn_func))
2486                 spam_learners = g_slist_append(spam_learners, learn_func);
2487         if (mainwindow_get_mainwindow()) {
2488                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2489                 summary_set_menu_sensitive(
2490                         mainwindow_get_mainwindow()->summaryview);
2491                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2492         }
2493 }
2494
2495 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2496 {
2497         spam_learners = g_slist_remove(spam_learners, learn_func);
2498         if (mainwindow_get_mainwindow()) {
2499                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2500                 summary_set_menu_sensitive(
2501                         mainwindow_get_mainwindow()->summaryview);
2502                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2503         }
2504 }
2505
2506 gboolean procmsg_spam_can_learn(void)
2507 {
2508         return g_slist_length(spam_learners) > 0;
2509 }
2510
2511 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2512 {
2513         GSList *cur = spam_learners;
2514         int ret = 0;
2515         for (; cur; cur = cur->next) {
2516                 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2517                 ret |= func(info, list, spam);
2518         }
2519         return ret;
2520 }
2521
2522 static gchar *spam_folder_item = NULL;
2523 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2524 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2525 {
2526         g_free(spam_folder_item);
2527         if (item_identifier)
2528                 spam_folder_item = g_strdup(item_identifier);
2529         else
2530                 spam_folder_item = NULL;
2531         if (spam_get_folder_func != NULL)
2532                 procmsg_spam_get_folder_func = spam_get_folder_func;
2533         else
2534                 procmsg_spam_get_folder_func = NULL;
2535 }
2536
2537 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2538 {
2539         FolderItem *item = NULL;
2540         
2541         if (procmsg_spam_get_folder_func) 
2542                 item = procmsg_spam_get_folder_func(msginfo);
2543         if (item == NULL && spam_folder_item)
2544                 item = folder_find_item_from_identifier(spam_folder_item);
2545         if (item == NULL)
2546                 item = folder_get_default_trash();
2547         return item;
2548 }
2549
2550 static void item_has_queued_mails(FolderItem *item, gpointer data)
2551 {
2552         gboolean *result = (gboolean *)data;
2553         if (*result == TRUE)
2554                 return;
2555         if (folder_has_parent_of_type(item, F_QUEUE)) {
2556                 if (item->total_msgs == 0)
2557                         return;
2558                 else {
2559                         GSList *msglist = folder_item_get_msg_list(item);
2560                         GSList *cur;
2561                         for (cur = msglist; cur; cur = cur->next) {
2562                                 MsgInfo *msginfo = (MsgInfo *)cur->data;
2563                                 if (!MSG_IS_DELETED(msginfo->flags) &&
2564                                     !MSG_IS_LOCKED(msginfo->flags)) {
2565                                         *result = TRUE;
2566                                         break;
2567                                 }
2568                         }
2569                         procmsg_msg_list_free(msglist);
2570                 }
2571         }
2572 }
2573
2574 gboolean procmsg_have_queued_mails_fast (void)
2575 {
2576         gboolean result = FALSE;
2577         folder_func_to_all_folders(item_has_queued_mails, &result);
2578         return result;
2579 }
2580
2581 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2582 {
2583         gboolean *result = (gboolean *)data;
2584         if (*result == TRUE)
2585                 return;
2586         if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2587                 *result = TRUE;
2588 }
2589
2590 gboolean procmsg_have_trashed_mails_fast (void)
2591 {
2592         gboolean result = FALSE;
2593         folder_func_to_all_folders(item_has_trashed_mails, &result);
2594         return result;
2595 }
2596
2597 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2598 {
2599         GSList *cur = NULL;
2600         gchar *tags = NULL;
2601         
2602         if (!msginfo)
2603                 return NULL;
2604
2605         if (msginfo->tags == NULL)
2606                 return NULL;
2607         for (cur = msginfo->tags; cur; cur = cur->next) {
2608                 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2609                 if (!tag)
2610                         continue;
2611                 if (!tags)
2612                         tags = g_strdup(tag);
2613                 else {
2614                         int olen = strlen(tags);
2615                         int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2616                         tags = g_realloc(tags, nlen+1);
2617                         if (!tags)
2618                                 return NULL;
2619                         strcpy(tags+olen, ", ");
2620                         strcpy(tags+olen+2, tag);
2621                         tags[nlen]='\0';
2622                 }
2623         }
2624         return tags;
2625 }
2626
2627 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2628 {
2629         GSList changed;
2630
2631         if (id == 0)
2632                 return;
2633
2634         if (!set) {
2635                 msginfo->tags = g_slist_remove(
2636                                         msginfo->tags,
2637                                         GINT_TO_POINTER(id));
2638                 changed.data = GINT_TO_POINTER(id);
2639                 changed.next = NULL;
2640                 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2641         } else {
2642                 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2643                         msginfo->tags = g_slist_append(
2644                                         msginfo->tags,
2645                                         GINT_TO_POINTER(id));
2646                 }
2647                 changed.data = GINT_TO_POINTER(id);
2648                 changed.next = NULL;
2649                 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2650         }
2651         
2652 }
2653
2654 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2655 {
2656         GSList *unset = msginfo->tags;
2657         msginfo->tags = NULL;
2658         folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2659         g_slist_free(unset);
2660 }