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