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