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