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