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