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