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