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