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