2011-12-28 [paul] 3.8.0cvs7
[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                                 if (replymessageid != NULL) {
1788                                         MsgPermFlags to_unset = 0;
1789
1790                                         if (prefs_common.mark_as_read_on_new_window)
1791                                                 to_unset = (MSG_NEW|MSG_UNREAD);
1792
1793                                         procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1794                                         procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1795                                 }  else {
1796                                         procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1797                                 }
1798                                 procmsg_msginfo_free(msginfo);
1799                         }
1800                 }
1801                 g_strfreev(tokens);
1802         }
1803
1804         g_free(from);
1805         g_free(smtpserver);
1806         slist_free_strings(to_list);
1807         g_slist_free(to_list);
1808         slist_free_strings(newsgroup_list);
1809         g_slist_free(newsgroup_list);
1810         g_free(savecopyfolder);
1811         g_free(replymessageid);
1812         g_free(fwdmessageid);
1813         g_free(privacy_system);
1814         g_free(encrypt_data);
1815
1816         return (newsval != 0 ? newsval : mailval);
1817 }
1818
1819 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1820 {
1821         gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1822         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1823         return result;
1824 }
1825
1826 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1827 {
1828         gint val;
1829         if (procmsg_queue_lock(errstr)) {
1830                 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1831                 procmsg_queue_unlock();
1832                 return val;
1833         }
1834         return -1;
1835 }
1836
1837 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1838 {
1839         MsgPermFlags new_flags = msginfo->flags.perm_flags;
1840
1841         /* NEW flag */
1842         if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1843                 item->new_msgs++;
1844         }
1845
1846         if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1847                 item->new_msgs--;
1848         }
1849
1850         /* UNREAD flag */
1851         if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1852                 item->unread_msgs++;
1853                 if (procmsg_msg_has_marked_parent(msginfo))
1854                         item->unreadmarked_msgs++;
1855         }
1856
1857         if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1858                 item->unread_msgs--;
1859                 if (procmsg_msg_has_marked_parent(msginfo))
1860                         item->unreadmarked_msgs--;
1861         }
1862         
1863         /* MARK flag */
1864         if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1865                 procmsg_update_unread_children(msginfo, TRUE);
1866                 item->marked_msgs++;
1867         }
1868
1869         if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1870                 procmsg_update_unread_children(msginfo, FALSE);
1871                 item->marked_msgs--;
1872         }
1873
1874         if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1875                 item->replied_msgs++;
1876         }
1877
1878         if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1879                 item->replied_msgs--;
1880         }
1881
1882         if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1883                 item->forwarded_msgs++;
1884         }
1885
1886         if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1887                 item->forwarded_msgs--;
1888         }
1889
1890         if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1891                 item->locked_msgs++;
1892         }
1893
1894         if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1895                 item->locked_msgs--;
1896         }
1897
1898         if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1899                 item->ignored_msgs--;
1900         }
1901
1902         if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1903                 item->ignored_msgs++;
1904         }
1905
1906         if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1907                 item->watched_msgs--;
1908         }
1909
1910         if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1911                 item->watched_msgs++;
1912         }
1913 }
1914
1915 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1916 {
1917         FolderItem *item;
1918         MsgInfoUpdate msginfo_update;
1919         MsgPermFlags perm_flags_new, perm_flags_old;
1920         MsgTmpFlags tmp_flags_old;
1921
1922         cm_return_if_fail(msginfo != NULL);
1923         item = msginfo->folder;
1924         cm_return_if_fail(item != NULL);
1925         
1926         debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1927
1928         /* Perm Flags handling */
1929         perm_flags_old = msginfo->flags.perm_flags;
1930         perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1931         if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1932                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1933         }
1934         if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1935                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1936         }
1937
1938         if (perm_flags_old != perm_flags_new) {
1939                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1940
1941                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1942                 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1943         }
1944
1945         /* Tmp flags handling */
1946         tmp_flags_old = msginfo->flags.tmp_flags;
1947         msginfo->flags.tmp_flags |= tmp_flags;
1948
1949         /* update notification */
1950         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1951                 msginfo_update.msginfo = msginfo;
1952                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1953                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1954                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1955         }
1956 }
1957
1958 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1959 {
1960         FolderItem *item;
1961         MsgInfoUpdate msginfo_update;
1962         MsgPermFlags perm_flags_new, perm_flags_old;
1963         MsgTmpFlags tmp_flags_old;
1964
1965         cm_return_if_fail(msginfo != NULL);
1966         item = msginfo->folder;
1967         cm_return_if_fail(item != NULL);
1968         
1969         debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1970
1971         /* Perm Flags handling */
1972         perm_flags_old = msginfo->flags.perm_flags;
1973         perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1974         
1975         if (perm_flags_old != perm_flags_new) {
1976                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1977
1978                 update_folder_msg_counts(item, msginfo, perm_flags_old);
1979         }
1980
1981         /* Tmp flags hanlding */
1982         tmp_flags_old = msginfo->flags.tmp_flags;
1983         msginfo->flags.tmp_flags &= ~tmp_flags;
1984
1985         /* update notification */
1986         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1987                 msginfo_update.msginfo = msginfo;
1988                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1989                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1990                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1991         }
1992 }
1993
1994 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 
1995                                 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1996                                 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1997 {
1998         FolderItem *item;
1999         MsgInfoUpdate msginfo_update;
2000         MsgPermFlags perm_flags_new, perm_flags_old;
2001         MsgTmpFlags tmp_flags_old;
2002
2003         cm_return_if_fail(msginfo != NULL);
2004         item = msginfo->folder;
2005         cm_return_if_fail(item != NULL);
2006         
2007         debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2008
2009         /* Perm Flags handling */
2010         perm_flags_old = msginfo->flags.perm_flags;
2011         perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2012         if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2013                 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2014         }
2015         if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2016                 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2017         }
2018
2019         if (perm_flags_old != perm_flags_new) {
2020                 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2021
2022                 update_folder_msg_counts(item, msginfo, perm_flags_old);
2023
2024         }
2025
2026         /* Tmp flags handling */
2027         tmp_flags_old = msginfo->flags.tmp_flags;
2028         msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2029         msginfo->flags.tmp_flags |= add_tmp_flags;
2030
2031         /* update notification */
2032         if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2033                 msginfo_update.msginfo = msginfo;
2034                 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2035                 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2036                 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2037         }
2038 }
2039
2040 /*!
2041  *\brief        check for flags (e.g. mark) in prior msgs of current thread
2042  *
2043  *\param        info Current message
2044  *\param        perm_flags Flags to be checked
2045  *\param        parentmsgs Hash of prior msgs to avoid loops
2046  *
2047  *\return       gboolean TRUE if perm_flags are found
2048  */
2049 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2050                 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2051 {
2052         MsgInfo *tmp;
2053
2054         cm_return_val_if_fail(info != NULL, FALSE);
2055
2056         if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2057                 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2058                                 info->inreplyto);
2059                 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2060                         procmsg_msginfo_free(tmp);
2061                         return TRUE;
2062                 } else if (tmp != NULL) {
2063                         gboolean result;
2064
2065                         if (g_hash_table_lookup(parentmsgs, info)) {
2066                                 debug_print("loop detected: %d\n",
2067                                         info->msgnum);
2068                                 result = FALSE;
2069                         } else {
2070                                 g_hash_table_insert(parentmsgs, info, "1");
2071                                 result = procmsg_msg_has_flagged_parent_real(
2072                                     tmp, perm_flags, parentmsgs);
2073                         }
2074                         procmsg_msginfo_free(tmp);
2075                         return result;
2076                 } else {
2077                         return FALSE;
2078                 }
2079         } else
2080                 return FALSE;
2081 }
2082
2083 /*!
2084  *\brief        Callback for cleaning up hash of parentmsgs
2085  */
2086 static gboolean parentmsgs_hash_remove(gpointer key,
2087                             gpointer value,
2088                             gpointer user_data)
2089 {
2090         return TRUE;
2091 }
2092
2093 /*!
2094  *\brief        Set up list of parentmsgs
2095  *              See procmsg_msg_has_flagged_parent_real()
2096  */
2097 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2098 {
2099         gboolean result;
2100         static GHashTable *parentmsgs = NULL;
2101         
2102         if (parentmsgs == NULL)
2103                 parentmsgs = g_hash_table_new(NULL, NULL); 
2104
2105         result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2106         g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2107
2108         return result;
2109 }
2110
2111 /*!
2112  *\brief        Check if msgs prior in thread are marked
2113  *              See procmsg_msg_has_flagged_parent_real()
2114  */
2115 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2116 {
2117         return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2118 }
2119
2120
2121 static GSList *procmsg_find_children_func(MsgInfo *info, 
2122                                    GSList *children, GSList *all)
2123 {
2124         GSList *cur;
2125
2126         cm_return_val_if_fail(info!=NULL, children);
2127         if (info->msgid == NULL)
2128                 return children;
2129
2130         for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2131                 MsgInfo *tmp = (MsgInfo *)cur->data;
2132                 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2133                         /* Check if message is already in the list */
2134                         if ((children == NULL) || 
2135                             (g_slist_index(children, tmp) == -1)) {
2136                                 children = g_slist_prepend(children,
2137                                                 procmsg_msginfo_new_ref(tmp));
2138                                 children = procmsg_find_children_func(tmp, 
2139                                                         children, 
2140                                                         all);
2141                         }
2142                 }
2143         }
2144         return children;
2145 }
2146
2147 static GSList *procmsg_find_children (MsgInfo *info)
2148 {
2149         GSList *children;
2150         GSList *all, *cur;
2151
2152         cm_return_val_if_fail(info!=NULL, NULL);
2153         all = folder_item_get_msg_list(info->folder);
2154         children = procmsg_find_children_func(info, NULL, all);
2155         if (children != NULL) {
2156                 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2157                         /* this will not free the used pointers
2158                            created with procmsg_msginfo_new_ref */
2159                         procmsg_msginfo_free((MsgInfo *)cur->data);
2160                 }
2161         }
2162         g_slist_free(all);
2163
2164         return children;
2165 }
2166
2167 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2168 {
2169         GSList *children = procmsg_find_children(info);
2170         GSList *cur;
2171         for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2172                 MsgInfo *tmp = (MsgInfo *)cur->data;
2173                 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2174                         if(newly_marked) 
2175                                 info->folder->unreadmarked_msgs++;
2176                         else
2177                                 info->folder->unreadmarked_msgs--;
2178                         folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2179                 }
2180                 procmsg_msginfo_free(tmp);
2181         }
2182         g_slist_free(children);
2183 }
2184
2185 /**
2186  * Set the destination folder for a copy or move operation
2187  *
2188  * \param msginfo The message which's destination folder is changed
2189  * \param to_folder The destination folder for the operation
2190  */
2191 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2192 {
2193         if(msginfo->to_folder != NULL) {
2194                 msginfo->to_folder->op_count--;
2195                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2196         }
2197         msginfo->to_folder = to_folder;
2198         if(to_folder != NULL) {
2199                 to_folder->op_count++;
2200                 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2201         }
2202 }
2203
2204 /**
2205  * Apply filtering actions to the msginfo
2206  *
2207  * \param msginfo The MsgInfo describing the message that should be filtered
2208  * \return TRUE if the message was moved and MsgInfo is now invalid,
2209  *         FALSE otherwise
2210  */
2211 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2212 {
2213         MailFilteringData mail_filtering_data;
2214                         
2215         mail_filtering_data.msginfo = msginfo;                  
2216         mail_filtering_data.msglist = NULL;                     
2217         mail_filtering_data.filtered = NULL;                    
2218         mail_filtering_data.unfiltered = NULL;
2219         mail_filtering_data.account = ac_prefs; 
2220
2221         if (!ac_prefs || ac_prefs->filterhook_on_recv)
2222                 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2223                 return TRUE;
2224
2225         /* filter if enabled in prefs or move to inbox if not */
2226         if((filtering_rules != NULL) &&
2227                 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2228                                 FILTERING_INCORPORATION, NULL)) {
2229                 return TRUE;
2230         }
2231                 
2232         return FALSE;
2233 }
2234
2235 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac, 
2236                             GSList **filtered, GSList **unfiltered,
2237                             gboolean do_filter)
2238 {
2239         GSList *cur, *to_do = NULL;
2240         gint total = 0, curnum = 0;
2241         MailFilteringData mail_filtering_data;
2242                         
2243         cm_return_if_fail(filtered != NULL);
2244         cm_return_if_fail(unfiltered != NULL);
2245
2246         *filtered = NULL;
2247         *unfiltered = NULL;
2248         
2249         if (list == NULL)
2250                 return;
2251
2252         total = g_slist_length(list);
2253
2254         if (!do_filter) {
2255                 *filtered = NULL;
2256                 *unfiltered = g_slist_copy(list);
2257                 return;
2258         }
2259
2260         statusbar_print_all(_("Filtering messages...\n"));
2261
2262         mail_filtering_data.msginfo = NULL;                     
2263         mail_filtering_data.msglist = list;                     
2264         mail_filtering_data.filtered = NULL;                    
2265         mail_filtering_data.unfiltered = NULL;  
2266         mail_filtering_data.account = ac;       
2267                         
2268         if (!ac || ac->filterhook_on_recv)
2269         hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2270         
2271         if (mail_filtering_data.filtered == NULL &&
2272             mail_filtering_data.unfiltered == NULL) {
2273                 /* nothing happened */
2274                 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2275                 to_do = list;
2276         } 
2277         if (mail_filtering_data.filtered != NULL) {
2278                 /* keep track of what's been filtered by the hooks */
2279                 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2280                         g_slist_length(list),
2281                         g_slist_length(mail_filtering_data.filtered),
2282                         g_slist_length(mail_filtering_data.unfiltered));
2283
2284                 *filtered = g_slist_copy(mail_filtering_data.filtered);
2285         }
2286         if (mail_filtering_data.unfiltered != NULL) {
2287                 /* what the hooks didn't handle will go in filtered or 
2288                  * unfiltered in the next loop */
2289                 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2290                         g_slist_length(list),
2291                         g_slist_length(mail_filtering_data.filtered),
2292                         g_slist_length(mail_filtering_data.unfiltered));
2293                 to_do = mail_filtering_data.unfiltered;
2294         } 
2295
2296         for (cur = to_do; cur; cur = cur->next) {
2297                 MsgInfo *info = (MsgInfo *)cur->data;
2298                 if (procmsg_msginfo_filter(info, ac))
2299                         *filtered = g_slist_prepend(*filtered, info);
2300                 else
2301                         *unfiltered = g_slist_prepend(*unfiltered, info);
2302                 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2303         }
2304
2305         g_slist_free(mail_filtering_data.filtered);
2306         g_slist_free(mail_filtering_data.unfiltered);
2307         
2308         *filtered = g_slist_reverse(*filtered);
2309         *unfiltered = g_slist_reverse(*unfiltered);
2310
2311         statusbar_progress_all(0,0,0);
2312         statusbar_pop_all();
2313 }
2314
2315 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2316 {
2317         MsgInfo *tmp_msginfo = NULL;
2318         MsgFlags flags = {0, 0};
2319         gchar *tmpfile = get_tmp_file();
2320         FILE *fp = g_fopen(tmpfile, "wb");
2321         
2322         if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2323             g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2324                 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2325                 if (fp) 
2326                         fclose(fp);
2327                 g_free(tmpfile);
2328                 return NULL;
2329         }
2330         
2331         if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2332                 fclose(fp);
2333                 fp = NULL;
2334                 tmp_msginfo = procheader_parse_file(
2335                         tmpfile, flags, 
2336                         TRUE, FALSE);
2337         }
2338         if (fp)
2339                 fclose(fp);
2340
2341         if (tmp_msginfo != NULL) {
2342                 if (src_msginfo)
2343                         tmp_msginfo->folder = src_msginfo->folder;
2344                 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2345         } else {
2346                 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2347         }
2348
2349         g_free(tmpfile);
2350
2351         return tmp_msginfo;
2352 }
2353
2354 static GSList *spam_learners = NULL;
2355
2356 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2357 {
2358         if (!g_slist_find(spam_learners, learn_func))
2359                 spam_learners = g_slist_append(spam_learners, learn_func);
2360         if (mainwindow_get_mainwindow()) {
2361                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2362                 summary_set_menu_sensitive(
2363                         mainwindow_get_mainwindow()->summaryview);
2364                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2365         }
2366 }
2367
2368 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2369 {
2370         spam_learners = g_slist_remove(spam_learners, learn_func);
2371         if (mainwindow_get_mainwindow()) {
2372                 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2373                 summary_set_menu_sensitive(
2374                         mainwindow_get_mainwindow()->summaryview);
2375                 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2376         }
2377 }
2378
2379 gboolean procmsg_spam_can_learn(void)
2380 {
2381         return g_slist_length(spam_learners) > 0;
2382 }
2383
2384 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2385 {
2386         GSList *cur = spam_learners;
2387         int ret = 0;
2388         for (; cur; cur = cur->next) {
2389                 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2390                 ret |= func(info, list, spam);
2391         }
2392         return ret;
2393 }
2394
2395 static gchar *spam_folder_item = NULL;
2396 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2397 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2398 {
2399         g_free(spam_folder_item);
2400         if (item_identifier)
2401                 spam_folder_item = g_strdup(item_identifier);
2402         else
2403                 spam_folder_item = NULL;
2404         if (spam_get_folder_func != NULL)
2405                 procmsg_spam_get_folder_func = spam_get_folder_func;
2406         else
2407                 procmsg_spam_get_folder_func = NULL;
2408 }
2409
2410 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2411 {
2412         FolderItem *item = NULL;
2413         
2414         if (procmsg_spam_get_folder_func) 
2415                 item = procmsg_spam_get_folder_func(msginfo);
2416         if (item == NULL && spam_folder_item)
2417                 item = folder_find_item_from_identifier(spam_folder_item);
2418         if (item == NULL)
2419                 item = folder_get_default_trash();
2420         return item;
2421 }
2422
2423 static void item_has_queued_mails(FolderItem *item, gpointer data)
2424 {
2425         gboolean *result = (gboolean *)data;
2426         if (*result == TRUE)
2427                 return;
2428         if (folder_has_parent_of_type(item, F_QUEUE)) {
2429                 if (item->total_msgs == 0)
2430                         return;
2431                 else {
2432                         GSList *msglist = folder_item_get_msg_list(item);
2433                         GSList *cur;
2434                         for (cur = msglist; cur; cur = cur->next) {
2435                                 MsgInfo *msginfo = (MsgInfo *)cur->data;
2436                                 if (!MSG_IS_DELETED(msginfo->flags) &&
2437                                     !MSG_IS_LOCKED(msginfo->flags)) {
2438                                         *result = TRUE;
2439                                         break;
2440                                 }
2441                         }
2442                         procmsg_msg_list_free(msglist);
2443                 }
2444         }
2445 }
2446
2447 gboolean procmsg_have_queued_mails_fast (void)
2448 {
2449         gboolean result = FALSE;
2450         folder_func_to_all_folders(item_has_queued_mails, &result);
2451         return result;
2452 }
2453
2454 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2455 {
2456         gboolean *result = (gboolean *)data;
2457         if (*result == TRUE)
2458                 return;
2459         if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2460                 *result = TRUE;
2461 }
2462
2463 gboolean procmsg_have_trashed_mails_fast (void)
2464 {
2465         gboolean result = FALSE;
2466         folder_func_to_all_folders(item_has_trashed_mails, &result);
2467         return result;
2468 }
2469
2470 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2471 {
2472         GSList *cur = NULL;
2473         gchar *tags = NULL;
2474         
2475         if (!msginfo)
2476                 return NULL;
2477
2478         if (msginfo->tags == NULL)
2479                 return NULL;
2480         for (cur = msginfo->tags; cur; cur = cur->next) {
2481                 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2482                 if (!tag)
2483                         continue;
2484                 if (!tags)
2485                         tags = g_strdup(tag);
2486                 else {
2487                         int olen = strlen(tags);
2488                         int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2489                         tags = g_realloc(tags, nlen+1);
2490                         if (!tags)
2491                                 return NULL;
2492                         strcpy(tags+olen, ", ");
2493                         strcpy(tags+olen+2, tag);
2494                         tags[nlen]='\0';
2495                 }
2496         }
2497         return tags;
2498 }
2499
2500 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2501 {
2502         GSList changed;
2503
2504         if (id == 0)
2505                 return;
2506
2507         if (!set) {
2508                 msginfo->tags = g_slist_remove(
2509                                         msginfo->tags,
2510                                         GINT_TO_POINTER(id));
2511                 changed.data = GINT_TO_POINTER(id);
2512                 changed.next = NULL;
2513                 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2514         } else {
2515                 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2516                         msginfo->tags = g_slist_append(
2517                                         msginfo->tags,
2518                                         GINT_TO_POINTER(id));
2519                 }
2520                 changed.data = GINT_TO_POINTER(id);
2521                 changed.next = NULL;
2522                 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2523         }
2524         
2525 }
2526
2527 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2528 {
2529         GSList *unset = msginfo->tags;
2530         msginfo->tags = NULL;
2531         folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2532         g_slist_free(unset);
2533 }