0ae7ab63a4f9d197c6533dadd678652c756fce3a
[claws.git] / src / procmsg.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "intl.h"
27 #include "main.h"
28 #include "utils.h"
29 #include "procmsg.h"
30 #include "procheader.h"
31 #include "send.h"
32 #include "procmime.h"
33 #include "statusbar.h"
34 #include "folder.h"
35 #include "prefs_common.h"
36 #include "account.h"
37 #if USE_GPGME
38 #  include "rfc2015.h"
39 #endif
40 #include "alertpanel.h"
41 #include "news.h"
42
43 typedef struct _FlagInfo        FlagInfo;
44
45 struct _FlagInfo
46 {
47         guint    msgnum;
48         MsgFlags flags;
49 };
50
51 static void mark_sum_func                       (gpointer        key,
52                                                  gpointer        value,
53                                                  gpointer        data);
54
55 static GHashTable *procmsg_read_mark_file       (const gchar    *folder);
56 static gint procmsg_cmp_msgnum                  (gconstpointer   a,
57                                                  gconstpointer   b);
58 static gint procmsg_cmp_flag_msgnum             (gconstpointer   a,
59                                                  gconstpointer   b);
60
61
62 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
63 {
64         GHashTable *msg_table;
65
66         if (mlist == NULL) return NULL;
67
68         msg_table = g_hash_table_new(NULL, g_direct_equal);
69         procmsg_msg_hash_table_append(msg_table, mlist);
70
71         return msg_table;
72 }
73
74 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
75 {
76         GSList *cur;
77         MsgInfo *msginfo;
78
79         if (msg_table == NULL || mlist == NULL) return;
80
81         for (cur = mlist; cur != NULL; cur = cur->next) {
82                 msginfo = (MsgInfo *)cur->data;
83
84                 g_hash_table_insert(msg_table,
85                                     GUINT_TO_POINTER(msginfo->msgnum),
86                                     msginfo);
87         }
88 }
89
90 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
91 {
92         GHashTable *msg_table;
93         GSList *cur;
94         MsgInfo *msginfo;
95
96         if (mlist == NULL) return NULL;
97
98         msg_table = g_hash_table_new(NULL, g_direct_equal);
99
100         for (cur = mlist; cur != NULL; cur = cur->next) {
101                 msginfo = (MsgInfo *)cur->data;
102                 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
103         }
104
105         return msg_table;
106 }
107
108 static gint procmsg_read_cache_data_str(FILE *fp, gchar **str)
109 {
110         gchar buf[BUFFSIZE];
111         gint ret = 0;
112         size_t len;
113
114         if (fread(&len, sizeof(len), 1, fp) == 1) {
115                 if (len < 0)
116                         ret = -1;
117                 else {
118                         gchar *tmp = NULL;
119
120                         while (len > 0) {
121                                 size_t size = MIN(len, BUFFSIZE - 1);
122
123                                 if (fread(buf, size, 1, fp) != 1) {
124                                         ret = -1;
125                                         if (tmp) g_free(tmp);
126                                         *str = NULL;
127                                         break;
128                                 }
129
130                                 buf[size] = '\0';
131                                 if (tmp) {
132                                         *str = g_strconcat(tmp, buf, NULL);
133                                         g_free(tmp);
134                                         tmp = *str;
135                                 } else
136                                         tmp = *str = g_strdup(buf);
137
138                                 len -= size;
139                         }
140                 }
141         } else
142                 ret = -1;
143
144         if (ret < 0)
145                 g_warning(_("Cache data is corrupted\n"));
146
147         return ret;
148 }
149
150 #define READ_CACHE_DATA(data, fp) \
151 { \
152         if (procmsg_read_cache_data_str(fp, &data) < 0) { \
153                 procmsg_msginfo_free(msginfo); \
154                 break; \
155         } \
156 }
157
158 #define READ_CACHE_DATA_INT(n, fp) \
159 { \
160         if (fread(&n, sizeof(n), 1, fp) != 1) { \
161                 g_warning(_("Cache data is corrupted\n")); \
162                 procmsg_msginfo_free(msginfo); \
163                 break; \
164         } \
165 }
166
167 GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file)
168 {
169         GSList *mlist = NULL;
170         GSList *last = NULL;
171         gchar *cache_file;
172         FILE *fp;
173         MsgInfo *msginfo;
174         MsgFlags default_flags;
175         gchar file_buf[BUFFSIZE];
176         gint ver;
177         guint num;
178         FolderType type;
179
180         g_return_val_if_fail(item != NULL, NULL);
181         g_return_val_if_fail(item->folder != NULL, NULL);
182         type = item->folder->type;
183
184         default_flags.perm_flags = MSG_NEW|MSG_UNREAD;
185         default_flags.tmp_flags = MSG_CACHED;
186         if (type == F_MH || type == F_IMAP) {
187                 if (item->stype == F_QUEUE) {
188                         MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED);
189                 } else if (item->stype == F_DRAFT) {
190                         MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT);
191                 }
192         }
193         if (type == F_IMAP) {
194                 MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP);
195         } else if (type == F_NEWS) {
196                 MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS);
197         }
198
199         if (type == F_MH) {
200                 gchar *path;
201
202                 path = folder_item_get_path(item);
203                 if (change_dir(path) < 0) {
204                         g_free(path);
205                         return NULL;
206                 }
207                 g_free(path);
208         }
209         cache_file = folder_item_get_cache_file(item);
210         if ((fp = fopen(cache_file, "rb")) == NULL) {
211                 debug_print(_("\tNo cache file\n"));
212                 g_free(cache_file);
213                 return NULL;
214         }
215         setvbuf(fp, file_buf, _IOFBF, sizeof(file_buf));
216         g_free(cache_file);
217
218         debug_print(_("\tReading summary cache...\n"));
219
220         /* compare cache version */
221         if (fread(&ver, sizeof(ver), 1, fp) != 1 ||
222             CACHE_VERSION != ver) {
223                 debug_print(_("Cache version is different. Discarding it.\n"));
224                 fclose(fp);
225                 return NULL;
226         }
227
228         while (fread(&num, sizeof(num), 1, fp) == 1) {
229                 msginfo = procmsg_msginfo_new();
230                 msginfo->msgnum = num;
231                 READ_CACHE_DATA_INT(msginfo->size, fp);
232                 READ_CACHE_DATA_INT(msginfo->mtime, fp);
233                 READ_CACHE_DATA_INT(msginfo->date_t, fp);
234                 READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
235
236                 READ_CACHE_DATA(msginfo->fromname, fp);
237
238                 READ_CACHE_DATA(msginfo->date, fp);
239                 READ_CACHE_DATA(msginfo->from, fp);
240                 READ_CACHE_DATA(msginfo->to, fp);
241                 READ_CACHE_DATA(msginfo->cc, fp);
242                 READ_CACHE_DATA(msginfo->newsgroups, fp);
243                 READ_CACHE_DATA(msginfo->subject, fp);
244                 READ_CACHE_DATA(msginfo->msgid, fp);
245                 READ_CACHE_DATA(msginfo->inreplyto, fp);
246                 READ_CACHE_DATA(msginfo->references, fp);
247                 READ_CACHE_DATA(msginfo->xref, fp);
248
249
250                 MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
251                 MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);
252
253                 /* if the message file doesn't exist or is changed,
254                    don't add the data */
255                 if (type == F_MH && scan_file &&
256                     folder_item_is_msg_changed(item, msginfo))
257                         procmsg_msginfo_free(msginfo);
258                 else {
259                         msginfo->folder = item;
260
261                         if (!mlist)
262                                 last = mlist = g_slist_append(NULL, msginfo);
263                         else {
264                                 last = g_slist_append(last, msginfo);
265                                 last = last->next;
266                         }
267                 }
268         }
269
270         fclose(fp);
271         debug_print(_("done.\n"));
272
273         return mlist;
274 }
275
276 #undef READ_CACHE_DATA
277 #undef READ_CACHE_DATA_INT
278
279 void procmsg_set_flags(GSList *mlist, FolderItem *item)
280 {
281         GSList *cur, *tmp;
282         gint newmsg = 0;
283         gint lastnum = 0;
284         gchar *markdir;
285         MsgInfo *msginfo;
286         GHashTable *mark_table;
287         MsgFlags *flags;
288
289         if (!mlist) return;
290         g_return_if_fail(item != NULL);
291         g_return_if_fail(item->folder != NULL);
292
293         debug_print(_("\tMarking the messages...\n"));
294
295         markdir = folder_item_get_path(item);
296         if (!is_dir_exist(markdir))
297                 make_dir_hier(markdir);
298
299         mark_table = procmsg_read_mark_file(markdir);
300         g_free(markdir);
301
302         if (!mark_table) return;
303
304         for (cur = mlist; cur != NULL; cur = cur->next) {
305                 msginfo = (MsgInfo *)cur->data;
306
307                 if (lastnum < msginfo->msgnum)
308                         lastnum = msginfo->msgnum;
309
310                 flags = g_hash_table_lookup
311                         (mark_table, GUINT_TO_POINTER(msginfo->msgnum));
312
313                 if (flags != NULL) {
314                         /* add the permanent flags only */
315                         msginfo->flags.perm_flags = flags->perm_flags;
316                         if (item->folder->type == F_IMAP) {
317                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP);
318                         } else if (item->folder->type == F_NEWS) {
319                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS);
320                         }
321                 } else {
322                         /* not found (new message) */
323                         if (newmsg == 0) {
324                                 for (tmp = mlist; tmp != cur; tmp = tmp->next)
325                                         MSG_UNSET_PERM_FLAGS
326                                                 (((MsgInfo *)tmp->data)->flags,
327                                                  MSG_NEW);
328                         }
329                         newmsg++;
330                 }
331         }
332
333         item->last_num = lastnum;
334
335         debug_print(_("done.\n"));
336         if (newmsg)
337                 debug_print(_("\t%d new message(s)\n"), newmsg);
338
339         hash_free_value_mem(mark_table);
340         g_hash_table_destroy(mark_table);
341 }
342
343 gint procmsg_get_last_num_in_cache(GSList *mlist)
344 {
345         GSList *cur;
346         MsgInfo *msginfo;
347         gint last = 0;
348
349         if (mlist == NULL) return 0;
350
351         for (cur = mlist; cur != NULL; cur = cur->next) {
352                 msginfo = (MsgInfo *)cur->data;
353                 if (msginfo && msginfo->msgnum > last)
354                         last = msginfo->msgnum;
355         }
356
357         return last;
358 }
359
360 void procmsg_msg_list_free(GSList *mlist)
361 {
362         GSList *cur;
363         MsgInfo *msginfo;
364
365         for (cur = mlist; cur != NULL; cur = cur->next) {
366                 msginfo = (MsgInfo *)cur->data;
367                 procmsg_msginfo_free(msginfo);
368         }
369         g_slist_free(mlist);
370 }
371
372 void procmsg_write_cache(MsgInfo *msginfo, FILE *fp)
373 {
374         MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
375
376         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
377         WRITE_CACHE_DATA_INT(msginfo->size, fp);
378         WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
379         WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
380         WRITE_CACHE_DATA_INT(flags, fp);
381
382         WRITE_CACHE_DATA(msginfo->fromname, fp);
383
384         WRITE_CACHE_DATA(msginfo->date, fp);
385         WRITE_CACHE_DATA(msginfo->from, fp);
386         WRITE_CACHE_DATA(msginfo->to, fp);
387         WRITE_CACHE_DATA(msginfo->cc, fp);
388         WRITE_CACHE_DATA(msginfo->newsgroups, fp);
389         WRITE_CACHE_DATA(msginfo->subject, fp);
390         WRITE_CACHE_DATA(msginfo->msgid, fp);
391         WRITE_CACHE_DATA(msginfo->inreplyto, fp);
392         WRITE_CACHE_DATA(msginfo->references, fp);
393         WRITE_CACHE_DATA(msginfo->xref, fp);
394
395 }
396
397 void procmsg_write_flags(MsgInfo *msginfo, FILE *fp)
398 {
399         MsgPermFlags flags = msginfo->flags.perm_flags;
400
401         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
402         WRITE_CACHE_DATA_INT(flags, fp);
403 }
404
405 void procmsg_flush_mark_queue(FolderItem *item, FILE *fp)
406 {
407         MsgInfo *flaginfo;
408
409         g_return_if_fail(item != NULL);
410         g_return_if_fail(fp != NULL);
411
412         while (item->mark_queue != NULL) {
413                 flaginfo = (MsgInfo *)item->mark_queue->data;
414                 procmsg_write_flags(flaginfo, fp);
415                 procmsg_msginfo_free(flaginfo);
416                 item->mark_queue = g_slist_remove(item->mark_queue, flaginfo);
417         }
418 }
419
420 void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags)
421 {
422         FILE *fp;
423         gchar *path;
424         MsgInfo msginfo;
425
426         g_return_if_fail(item != NULL);
427
428         if (item->opened) {
429                 MsgInfo *queue_msginfo;
430
431                 queue_msginfo = g_new0(MsgInfo, 1);
432                 queue_msginfo->msgnum = num;
433                 queue_msginfo->flags = flags;
434                 item->mark_queue = g_slist_append
435                         (item->mark_queue, queue_msginfo);
436                 return;
437         }
438
439         path = folder_item_get_path(item);
440         g_return_if_fail(path != NULL);
441
442         if ((fp = procmsg_open_mark_file(path, TRUE)) == NULL) {
443                 g_warning(_("can't open mark file\n"));
444                 g_free(path);
445                 return;
446         }
447         g_free(path);
448
449         msginfo.msgnum = num;
450         msginfo.flags = flags;
451
452         procmsg_write_flags(&msginfo, fp);
453         fclose(fp);
454 }
455
456 struct MarkSum {
457         gint *new;
458         gint *unread;
459         gint *total;
460         gint *min;
461         gint *max;
462         gint first;
463 };
464
465 static void mark_sum_func(gpointer key, gpointer value, gpointer data)
466 {
467         MsgFlags *flags = value;
468         gint num = GPOINTER_TO_INT(key);
469         struct MarkSum *marksum = data;
470
471         if (marksum->first <= num) {
472                 if (MSG_IS_NEW(*flags) && !MSG_IS_IGNORE_THREAD(*flags)) (*marksum->new)++;
473                 if (MSG_IS_UNREAD(*flags) && !MSG_IS_IGNORE_THREAD(*flags)) (*marksum->unread)++;
474                 if (num > *marksum->max) *marksum->max = num;
475                 if (num < *marksum->min || *marksum->min == 0) *marksum->min = num;
476                 (*marksum->total)++;
477         }
478
479         g_free(flags);
480 }
481
482 void procmsg_get_mark_sum(const gchar *folder,
483                           gint *new, gint *unread, gint *total,
484                           gint *min, gint *max,
485                           gint first)
486 {
487         GHashTable *mark_table;
488         struct MarkSum marksum;
489
490         *new = *unread = *total = *min = *max = 0;
491         marksum.new    = new;
492         marksum.unread = unread;
493         marksum.total  = total;
494         marksum.min    = min;
495         marksum.max    = max;
496         marksum.first  = first;
497
498         mark_table = procmsg_read_mark_file(folder);
499
500         if (mark_table) {
501                 g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
502                 g_hash_table_destroy(mark_table);
503         }
504         debug_print("mark->new = %d, mark->unread = %d, mark->total = %d\n",
505                     *(marksum.new), *(marksum.unread), *(marksum.total));
506 }
507
508 static GHashTable *procmsg_read_mark_file(const gchar *folder)
509 {
510         FILE *fp;
511         GHashTable *mark_table = NULL;
512         gint num;
513         MsgFlags *flags;
514         MsgPermFlags perm_flags;
515
516         if ((fp = procmsg_open_mark_file(folder, FALSE)) == NULL)
517                 return NULL;
518
519         mark_table = g_hash_table_new(NULL, g_direct_equal);
520
521         while (fread(&num, sizeof(num), 1, fp) == 1) {
522                 if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
523
524                 flags = g_new0(MsgFlags, 1);
525                 flags->perm_flags = perm_flags;
526     
527                 if(!MSG_IS_REALLY_DELETED(*flags)) {
528                         g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
529                 } else {
530                         g_hash_table_remove(mark_table, GUINT_TO_POINTER(num));
531                 }
532         }
533
534         fclose(fp);
535         return mark_table;
536 }
537
538 FILE *procmsg_open_mark_file(const gchar *folder, gboolean append)
539 {
540         gchar *markfile;
541         FILE *fp;
542         gint ver;
543
544         markfile = g_strconcat(folder, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
545
546         if ((fp = fopen(markfile, "rb")) == NULL)
547                 debug_print(_("Mark file not found.\n"));
548         else if (fread(&ver, sizeof(ver), 1, fp) != 1 || MARK_VERSION != ver) {
549                 debug_print(_("Mark version is different (%d != %d). "
550                               "Discarding it.\n"), ver, MARK_VERSION);
551                 fclose(fp);
552                 fp = NULL;
553         }
554
555         /* read mode */
556         if (append == FALSE) {
557                 g_free(markfile);
558                 return fp;
559         }
560
561         if (fp) {
562                 /* reopen with append mode */
563                 fclose(fp);
564                 if ((fp = fopen(markfile, "ab")) == NULL)
565                         g_warning(_("Can't open mark file with append mode.\n"));
566         } else {
567                 /* open with overwrite mode if mark file doesn't exist or
568                    version is different */
569                 if ((fp = fopen(markfile, "wb")) == NULL)
570                         g_warning(_("Can't open mark file with write mode.\n"));
571                 else {
572                         ver = MARK_VERSION;
573                         WRITE_CACHE_DATA_INT(ver, fp);
574                 }
575         }
576
577         g_free(markfile);
578         return fp;
579 }
580
581 /* return the reversed thread tree */
582 GNode *procmsg_get_thread_tree(GSList *mlist)
583 {
584         GNode *root, *parent, *node, *next;
585         GHashTable *msgid_table;
586         GHashTable *subject_table;
587         MsgInfo *msginfo;
588         const gchar *msgid;
589         const gchar *subject;
590
591         root = g_node_new(NULL);
592         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
593         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
594
595         for (; mlist != NULL; mlist = mlist->next) {
596                 msginfo = (MsgInfo *)mlist->data;
597                 parent = root;
598
599                 if (msginfo->inreplyto) {
600                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
601                         if (parent == NULL) {
602                                 parent = root;
603                         } else {
604                                 if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
605                                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
606                                 }
607                         }
608                 }
609                 node = g_node_insert_data_before
610                         (parent, parent == root ? parent->children : NULL,
611                          msginfo);
612                 if ((msgid = msginfo->msgid) &&
613                     g_hash_table_lookup(msgid_table, msgid) == NULL)
614                         g_hash_table_insert(msgid_table, (gchar *)msgid, node);
615
616                 subject = msginfo->subject;
617                 if (subject_table_lookup(subject_table,
618                                          (gchar *) subject) == NULL)
619                         subject_table_insert(subject_table, (gchar *)subject,
620                                              node);
621         }
622
623         /* complete the unfinished threads */
624         for (node = root->children; node != NULL; ) {
625                 next = node->next;
626                 msginfo = (MsgInfo *)node->data;
627                 parent = NULL;
628                 if (msginfo->inreplyto) 
629                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
630                 if (parent && parent != node) {
631                         g_node_unlink(node);
632                         g_node_insert_before
633                                 (parent, parent->children, node);
634                         /* CLAWS: ignore thread */
635                         if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
636                                 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
637                         }
638                 }
639                 node = next;
640         }
641
642         /* CLAWS: now see if the first level (below root) still has some nodes that can be
643          * threaded by subject line. we need to handle this in a special way to prevent
644          * circular reference from a node that has already been threaded by IN-REPLY-TO
645          * but is also in the subject line hash table */
646         for (node = root->children; node != NULL; ) {
647                 next = node->next;
648                 msginfo = (MsgInfo *) node->data;
649                 parent = NULL;
650                 if (subject_is_reply(msginfo->subject)) {
651                         parent = subject_table_lookup(subject_table,
652                                                       msginfo->subject);
653                         /* the node may already be threaded by IN-REPLY-TO,
654                            so go up in the tree to find the parent node */
655                         if (parent != NULL) {
656                                 if (g_node_is_ancestor(node, parent))
657                                         parent = NULL;
658                                 if (parent == node)
659                                         parent = NULL;
660                         }
661
662                         if (parent) {
663                                 g_node_unlink(node);
664                                 g_node_append(parent, node);
665                                 /* CLAWS: ignore thread */
666                                 if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
667                                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
668                                 }
669                         }
670                 }                                       
671                 node = next;
672         }               
673                 
674         g_hash_table_destroy(subject_table);
675         g_hash_table_destroy(msgid_table);
676
677         return root;
678 }
679
680 void procmsg_move_messages(GSList *mlist)
681 {
682         GSList *cur, *movelist = NULL;
683         MsgInfo *msginfo;
684         FolderItem *dest = NULL;
685         GHashTable *hash;
686
687         if (!mlist) return;
688
689         for (cur = mlist; cur != NULL; cur = cur->next) {
690                 msginfo = (MsgInfo *)cur->data;
691                 if (!dest) {
692                         dest = msginfo->to_folder;
693                         movelist = g_slist_append(movelist, msginfo);
694                 } else if (dest == msginfo->to_folder) {
695                         movelist = g_slist_append(movelist, msginfo);
696                 } else {
697                         folder_item_move_msgs_with_dest(dest, movelist);
698                         g_slist_free(movelist);
699                         movelist = NULL;
700                         dest = msginfo->to_folder;
701                         movelist = g_slist_append(movelist, msginfo);
702                 }
703         }
704
705         if (movelist) {
706                 folder_item_move_msgs_with_dest(dest, movelist);
707                 g_slist_free(movelist);
708         }
709 }
710
711 void procmsg_copy_messages(GSList *mlist)
712 {
713         GSList *cur, *copylist = NULL;
714         MsgInfo *msginfo;
715         FolderItem *dest = NULL;
716         GHashTable *hash;
717
718         if (!mlist) return;
719
720         /* 
721         
722         Horrible: Scanning 2 times for every copy!
723
724         hash = procmsg_to_folder_hash_table_create(mlist);
725         folder_item_scan_foreach(hash);
726         g_hash_table_destroy(hash);
727         */
728
729         for (cur = mlist; cur != NULL; cur = cur->next) {
730                 msginfo = (MsgInfo *)cur->data;
731                 if (!dest) {
732                         dest = msginfo->to_folder;
733                         copylist = g_slist_append(copylist, msginfo);
734                 } else if (dest == msginfo->to_folder) {
735                         copylist = g_slist_append(copylist, msginfo);
736                 } else {
737                         folder_item_copy_msgs_with_dest(dest, copylist);
738                         g_slist_free(copylist);
739                         copylist = NULL;
740                         dest = msginfo->to_folder;
741                         copylist = g_slist_append(copylist, msginfo);
742                 }
743         }
744
745         if (copylist) {
746                 folder_item_copy_msgs_with_dest(dest, copylist);
747                 g_slist_free(copylist);
748         }
749 }
750
751 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
752 {
753         gchar *path, *file;
754
755         g_return_val_if_fail(msginfo != NULL, NULL);
756
757         if (msginfo->plaintext_file)
758                 file = g_strdup(msginfo->plaintext_file);
759         else {
760                 path = folder_item_get_path(msginfo->folder);
761                 file = g_strconcat(path, G_DIR_SEPARATOR_S,
762                                    itos(msginfo->msgnum), NULL);
763                 g_free(path);
764         }
765
766         return file;
767 }
768
769 gchar *procmsg_get_message_file(MsgInfo *msginfo)
770 {
771         gchar *filename = NULL;
772
773         g_return_val_if_fail(msginfo != NULL, NULL);
774
775         filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
776         if (!filename)
777                 g_warning(_("can't fetch message %d\n"), msginfo->msgnum);
778
779         return filename;
780 }
781
782 FILE *procmsg_open_message(MsgInfo *msginfo)
783 {
784         FILE *fp;
785         gchar *file;
786
787         g_return_val_if_fail(msginfo != NULL, NULL);
788
789         file = procmsg_get_message_file_path(msginfo);
790         g_return_val_if_fail(file != NULL, NULL);
791
792         if (!is_file_exist(file)) {
793                 g_free(file);
794                 file = procmsg_get_message_file(msginfo);
795                 g_return_val_if_fail(file != NULL, NULL);
796         }
797
798         if ((fp = fopen(file, "rb")) == NULL) {
799                 FILE_OP_ERROR(file, "fopen");
800                 g_free(file);
801                 return NULL;
802         }
803
804         g_free(file);
805
806         if (MSG_IS_QUEUED(msginfo->flags)) {
807                 gchar buf[BUFFSIZE];
808
809                 while (fgets(buf, sizeof(buf), fp) != NULL)
810                         if (buf[0] == '\r' || buf[0] == '\n') break;
811         }
812
813         return fp;
814 }
815
816 #if USE_GPGME
817 FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
818 {
819         FILE *fp;
820         MimeInfo *mimeinfo_;
821
822         g_return_val_if_fail(msginfo != NULL, NULL);
823
824         if (mimeinfo) *mimeinfo = NULL;
825
826         if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL;
827
828         mimeinfo_ = procmime_scan_mime_header(fp);
829         if (!mimeinfo_) {
830                 fclose(fp);
831                 return NULL;
832         }
833
834         if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
835             rfc2015_is_encrypted(mimeinfo_)) {
836                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
837         }
838
839         if (MSG_IS_ENCRYPTED(msginfo->flags) &&
840             !msginfo->plaintext_file &&
841             !msginfo->decryption_failed) {
842                 rfc2015_decrypt_message(msginfo, mimeinfo_, fp);
843                 if (msginfo->plaintext_file &&
844                     !msginfo->decryption_failed) {
845                         fclose(fp);
846                         procmime_mimeinfo_free_all(mimeinfo_);
847                         if ((fp = procmsg_open_message(msginfo)) == NULL)
848                                 return NULL;
849                         mimeinfo_ = procmime_scan_mime_header(fp);
850                         if (!mimeinfo_) {
851                                 fclose(fp);
852                                 return NULL;
853                         }
854                 }
855         }
856
857         if (mimeinfo) *mimeinfo = mimeinfo_;
858         return fp;
859 }
860 #endif
861
862 gboolean procmsg_msg_exist(MsgInfo *msginfo)
863 {
864         gchar *path;
865         gboolean ret;
866
867         if (!msginfo) return FALSE;
868
869         path = folder_item_get_path(msginfo->folder);
870         change_dir(path);
871         ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
872         g_free(path);
873
874         return ret;
875 }
876
877 void procmsg_empty_trash(void)
878 {
879         FolderItem *trash;
880         GList *cur;
881
882         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
883                 trash = FOLDER(cur->data)->trash;
884                 if (trash && trash->total > 0)
885                         folder_item_remove_all_msg(trash);
886         }
887 }
888
889 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
890 {
891         gint i;
892         gint ret = 0;
893         GSList *list, *elem;
894
895         if (!queue)
896                 queue = folder_get_default_queue();
897         g_return_val_if_fail(queue != NULL, -1);
898
899         folder_item_scan(queue);
900         list = folder_item_get_msg_list(queue);
901
902
903         for(elem = list; elem != NULL; elem = elem->next) {
904                 gchar *file;
905                 MsgInfo *msginfo;
906                 
907                 msginfo = (MsgInfo *)(elem->data);
908
909                 file = folder_item_fetch_msg(queue, msginfo->msgnum);
910                 if (file) {
911                         if (procmsg_send_message_queue(file) < 0) {
912                                 g_warning(_("Sending queued message %d failed.\n"), msginfo->msgnum);
913                                 ret = -1;
914                         } else {
915                         /* CLAWS: 
916                          * We save in procmsg_send_message_queue because
917                          * we need the destination folder from the queue
918                          * header
919                                                 
920                                 if (save_msgs)
921                                         procmsg_save_to_outbox
922                                                 (queue->folder->outbox,
923                                                  file, TRUE);
924 */
925                                 folder_item_remove_msg(queue, msginfo->msgnum);
926                         }
927                         g_free(file);
928                 }
929                 procmsg_msginfo_free(msginfo);
930         }
931
932         folderview_update_item(queue, FALSE);
933
934         return ret;
935 }
936
937 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
938                             gboolean is_queued)
939 {
940         gint num;
941         FILE *fp;
942         MsgFlags flag = {0, 0};
943         MsgInfo *msginfo;
944
945         debug_print(_("saving sent message...\n"));
946
947         if (!outbox)
948                 outbox = folder_get_default_outbox();
949         g_return_val_if_fail(outbox != NULL, -1);
950
951         /* remove queueing headers */
952         if (is_queued) {
953                 gchar tmp[MAXPATHLEN + 1];
954                 gchar buf[BUFFSIZE];
955                 FILE *outfp;
956
957                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
958                            get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
959                 if ((fp = fopen(file, "rb")) == NULL) {
960                         FILE_OP_ERROR(file, "fopen");
961                         return -1;
962                 }
963                 if ((outfp = fopen(tmp, "wb")) == NULL) {
964                         FILE_OP_ERROR(tmp, "fopen");
965                         fclose(fp);
966                         return -1;
967                 }
968                 while (fgets(buf, sizeof(buf), fp) != NULL)
969                         if (buf[0] == '\r' || buf[0] == '\n') break;
970                 while (fgets(buf, sizeof(buf), fp) != NULL)
971                         fputs(buf, outfp);
972                 fclose(outfp);
973                 fclose(fp);
974                 Xstrdup_a(file, tmp, return -1);
975         }
976
977         if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
978                 g_warning(_("can't save message\n"));
979                 if(is_queued) {
980                         unlink(file);
981                 }
982                 return -1;
983         }
984         msginfo = folder_item_fetch_msginfo(outbox, num);
985         procmsg_msginfo_unset_flags(msginfo, ~0, ~0);
986         procmsg_msginfo_free(msginfo);
987
988         if(is_queued) {
989                 unlink(file);
990         }
991
992         return 0;
993 }
994
995 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
996 {
997         static const gchar *def_cmd = "lpr %s";
998         static guint id = 0;
999         gchar *prtmp;
1000         FILE *tmpfp, *prfp;
1001         gchar buf[1024];
1002         gchar *p;
1003
1004         g_return_if_fail(msginfo);
1005
1006         if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
1007                 g_warning(_("Can't get text part\n"));
1008                 return;
1009         }
1010
1011         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1012                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1013
1014         if ((prfp = fopen(prtmp, "wb")) == NULL) {
1015                 FILE_OP_ERROR(prtmp, "fopen");
1016                 g_free(prtmp);
1017                 fclose(tmpfp);
1018                 return;
1019         }
1020
1021         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1022         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1023         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
1024         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
1025         if (msginfo->newsgroups)
1026                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1027         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1028         fputc('\n', prfp);
1029
1030         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1031                 fputs(buf, prfp);
1032
1033         fclose(prfp);
1034         fclose(tmpfp);
1035
1036         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1037             !strchr(p + 2, '%'))
1038                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1039         else {
1040                 if (cmdline)
1041                         g_warning(_("Print command line is invalid: `%s'\n"),
1042                                   cmdline);
1043                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1044         }
1045
1046         g_free(prtmp);
1047
1048         g_strchomp(buf);
1049         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1050         system(buf);
1051 }
1052
1053 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1054 {
1055         msginfo->refcnt++;
1056         
1057         return msginfo;
1058 }
1059
1060 MsgInfo *procmsg_msginfo_new()
1061 {
1062         MsgInfo *newmsginfo;
1063
1064         newmsginfo = g_new0(MsgInfo, 1);
1065         newmsginfo->refcnt = 1;
1066         
1067         return newmsginfo;
1068 }
1069
1070 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1071 {
1072         MsgInfo *newmsginfo;
1073
1074         if (msginfo == NULL) return NULL;
1075
1076         newmsginfo = g_new0(MsgInfo, 1);
1077
1078         newmsginfo->refcnt = 1;
1079
1080 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1081 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1082                         g_strdup(msginfo->mmb) : NULL
1083
1084         MEMBCOPY(msgnum);
1085         MEMBCOPY(size);
1086         MEMBCOPY(mtime);
1087         MEMBCOPY(date_t);
1088         MEMBCOPY(flags);
1089
1090         MEMBDUP(fromname);
1091
1092         MEMBDUP(date);
1093         MEMBDUP(from);
1094         MEMBDUP(to);
1095         MEMBDUP(cc);
1096         MEMBDUP(newsgroups);
1097         MEMBDUP(subject);
1098         MEMBDUP(msgid);
1099         MEMBDUP(inreplyto);
1100         MEMBDUP(xref);
1101
1102         MEMBCOPY(folder);
1103         MEMBCOPY(to_folder);
1104
1105         MEMBDUP(xface);
1106         MEMBDUP(dispositionnotificationto);
1107         MEMBDUP(returnreceiptto);
1108         MEMBDUP(references);
1109
1110         MEMBCOPY(score);
1111         MEMBCOPY(threadscore);
1112
1113         return newmsginfo;
1114 }
1115
1116 void procmsg_msginfo_free(MsgInfo *msginfo)
1117 {
1118         if (msginfo == NULL) return;
1119
1120         msginfo->refcnt--;
1121         if(msginfo->refcnt > 0)
1122                 return;
1123
1124         g_free(msginfo->fromspace);
1125         g_free(msginfo->references);
1126         g_free(msginfo->returnreceiptto);
1127         g_free(msginfo->dispositionnotificationto);
1128         g_free(msginfo->xface);
1129
1130         g_free(msginfo->fromname);
1131
1132         g_free(msginfo->date);
1133         g_free(msginfo->from);
1134         g_free(msginfo->to);
1135         g_free(msginfo->cc);
1136         g_free(msginfo->newsgroups);
1137         g_free(msginfo->subject);
1138         g_free(msginfo->msgid);
1139         g_free(msginfo->inreplyto);
1140         g_free(msginfo->xref);
1141
1142         g_free(msginfo);
1143 }
1144
1145 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1146 {
1147         guint memusage = 0;
1148         
1149         memusage += sizeof(MsgInfo);
1150         if(msginfo->fromname)
1151                 memusage += strlen(msginfo->fromname);
1152         if(msginfo->date)
1153                 memusage += strlen(msginfo->date);
1154         if(msginfo->from)
1155                 memusage += strlen(msginfo->from);
1156         if(msginfo->to)
1157                 memusage += strlen(msginfo->to);
1158         if(msginfo->cc)
1159                 memusage += strlen(msginfo->cc);
1160         if(msginfo->newsgroups)
1161                 memusage += strlen(msginfo->newsgroups);
1162         if(msginfo->subject)
1163                 memusage += strlen(msginfo->subject);
1164         if(msginfo->msgid)
1165                 memusage += strlen(msginfo->msgid);
1166         if(msginfo->inreplyto)
1167                 memusage += strlen(msginfo->inreplyto);
1168         if(msginfo->xface)
1169                 memusage += strlen(msginfo->xface);
1170         if(msginfo->dispositionnotificationto)
1171                 memusage += strlen(msginfo->dispositionnotificationto);
1172         if(msginfo->returnreceiptto)
1173                 memusage += strlen(msginfo->returnreceiptto);
1174         if(msginfo->references)
1175                 memusage += strlen(msginfo->references);
1176         if(msginfo->fromspace)
1177                 memusage += strlen(msginfo->fromspace);
1178
1179         return memusage;
1180 }
1181
1182 static gint procmsg_cmp_msgnum(gconstpointer a, gconstpointer b)
1183 {
1184         const MsgInfo *msginfo = a;
1185         const guint msgnum = GPOINTER_TO_UINT(b);
1186
1187         if (!msginfo)
1188                 return -1;
1189
1190         return msginfo->msgnum - msgnum;
1191 }
1192
1193 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1194 {
1195         const MsgInfo *msginfo1 = a;
1196         const MsgInfo *msginfo2 = b;
1197
1198         if (!msginfo1)
1199                 return -1;
1200         if (!msginfo2)
1201                 return -1;
1202
1203         return msginfo1->msgnum - msginfo2->msgnum;
1204 }
1205
1206 static gint procmsg_cmp_flag_msgnum(gconstpointer a, gconstpointer b)
1207 {
1208         const FlagInfo *finfo = a;
1209         const guint msgnum = GPOINTER_TO_UINT(b);
1210
1211         if (!finfo)
1212                 return -1;
1213
1214         return finfo->msgnum - msgnum;
1215 }
1216
1217 enum
1218 {
1219         Q_SENDER           = 0,
1220         Q_SMTPSERVER       = 1,
1221         Q_RECIPIENTS       = 2,
1222         Q_NEWSGROUPS       = 3,
1223         Q_MAIL_ACCOUNT_ID  = 4,
1224         Q_NEWS_ACCOUNT_ID  = 5,
1225         Q_SAVE_COPY_FOLDER = 6
1226 };
1227
1228 gint procmsg_send_message_queue(const gchar *file)
1229 {
1230         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1231                                        {"SSV:",  NULL, FALSE},
1232                                        {"R:",    NULL, FALSE},
1233                                        {"NG:",   NULL, FALSE},
1234                                        {"MAID:", NULL, FALSE},
1235                                        {"NAID:", NULL, FALSE},
1236                                        {"SCF:",  NULL, FALSE},
1237                                        {NULL,    NULL, FALSE}};
1238         FILE *fp;
1239         gint filepos;
1240         gint mailval = 0, newsval = 0;
1241         gchar *from = NULL;
1242         gchar *smtpserver = NULL;
1243         GSList *to_list = NULL;
1244         GSList *newsgroup_list = NULL;
1245         gchar *savecopyfolder = NULL;
1246         gchar buf[BUFFSIZE];
1247         gint hnum;
1248         PrefsAccount *mailac = NULL, *newsac = NULL;
1249         int local = 0;
1250
1251         g_return_val_if_fail(file != NULL, -1);
1252
1253         if ((fp = fopen(file, "rb")) == NULL) {
1254                 FILE_OP_ERROR(file, "fopen");
1255                 return -1;
1256         }
1257
1258         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1259                != -1) {
1260                 gchar *p = buf + strlen(qentry[hnum].name);
1261
1262                 switch (hnum) {
1263                 case Q_SENDER:
1264                         if (!from) from = g_strdup(p);
1265                         break;
1266                 case Q_SMTPSERVER:
1267                         if (!smtpserver) smtpserver = g_strdup(p);
1268                         break;
1269                 case Q_RECIPIENTS:
1270                         to_list = address_list_append(to_list, p);
1271                         break;
1272                 case Q_NEWSGROUPS:
1273                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1274                         break;
1275                 case Q_MAIL_ACCOUNT_ID:
1276                         mailac = account_find_from_id(atoi(p));
1277                         break;
1278                 case Q_NEWS_ACCOUNT_ID:
1279                         newsac = account_find_from_id(atoi(p));
1280                         break;
1281                 case Q_SAVE_COPY_FOLDER:
1282                         if (!savecopyfolder) savecopyfolder = g_strdup(p);
1283                         break;
1284                 }
1285         }
1286         filepos = ftell(fp);
1287
1288         fseek(fp, filepos, SEEK_SET);
1289         if (to_list) {
1290                 debug_print(_("Sending message by mail\n"));
1291                 if(!from) {
1292                         g_warning(_("Queued message header is broken.\n"));
1293                         mailval = -1;
1294                 } else if (mailac && mailac->use_mail_command &&
1295                            mailac->mail_command && (* mailac->mail_command)) {
1296                         mailval = send_message_local(mailac->mail_command, fp);
1297                         local = 1;
1298                 } else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
1299                         mailval = send_message_local(prefs_common.extsend_cmd, fp);
1300                         local = 1;
1301                 } else {
1302                         if (!mailac) {
1303                                 mailac = account_find_from_smtp_server(from, smtpserver);
1304                                 if (!mailac) {
1305                                         g_warning(_("Account not found. "
1306                                                     "Using current account...\n"));
1307                                         mailac = cur_account;
1308                                 }
1309                         }
1310
1311                         if (mailac)
1312                                 mailval = send_message_smtp(mailac, to_list, fp);
1313                         else {
1314                                 PrefsAccount tmp_ac;
1315
1316                                 g_warning(_("Account not found.\n"));
1317
1318                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1319                                 tmp_ac.address = from;
1320                                 tmp_ac.smtp_server = smtpserver;
1321                                 tmp_ac.smtpport = SMTP_PORT;
1322                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1323                         }
1324                 }
1325                 if (mailval < 0) {
1326                         if (!local)
1327                                 alertpanel_error(
1328                                         _("Error occurred while sending the message to `%s'."),
1329                                         mailac ? mailac->smtp_server : smtpserver);
1330                         else
1331                                 alertpanel_error(
1332                                         _("Error occurred while sending the message with command `%s'."),
1333                                         (mailac && mailac->use_mail_command && 
1334                                          mailac->mail_command && (*mailac->mail_command)) ? 
1335                                                 mailac->mail_command : prefs_common.extsend_cmd);
1336                 }
1337         }
1338
1339         if(newsgroup_list && (newsval == 0)) {
1340                 Folder *folder;
1341                 gchar *tmp = NULL;
1342                 FILE *tmpfp;
1343
1344                 /* write to temporary file */
1345                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1346                             G_DIR_SEPARATOR, (gint)file);
1347                 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1348                         FILE_OP_ERROR(tmp, "fopen");
1349                         newsval = -1;
1350                         alertpanel_error(_("Could not create temporary file for news sending."));
1351                 } else {
1352                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1353                                 FILE_OP_ERROR(tmp, "chmod");
1354                                 g_warning(_("can't change file mode\n"));
1355                         }
1356
1357                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1358                                 if (fputs(buf, tmpfp) == EOF) {
1359                                         FILE_OP_ERROR(tmp, "fputs");
1360                                         newsval = -1;
1361                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1362                                 }
1363                         }
1364                         fclose(tmpfp);
1365
1366                         if(newsval == 0) {
1367                                 debug_print(_("Sending message by news\n"));
1368
1369                                 folder = FOLDER(newsac->folder);
1370
1371                                 newsval = news_post(folder, tmp);
1372                                 if (newsval < 0) {
1373                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1374                                                  newsac->nntp_server);
1375                                 }
1376                         }
1377                         unlink(tmp);
1378                 }
1379                 g_free(tmp);
1380         }
1381
1382         slist_free_strings(to_list);
1383         g_slist_free(to_list);
1384         slist_free_strings(newsgroup_list);
1385         g_slist_free(newsgroup_list);
1386         g_free(from);
1387         g_free(smtpserver);
1388         fclose(fp);
1389
1390         /* save message to outbox */
1391         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1392                 FolderItem *outbox;
1393
1394                 debug_print(_("saving sent message...\n"));
1395
1396                 outbox = folder_find_item_from_identifier(savecopyfolder);
1397                 if(!outbox)
1398                         outbox = folder_get_default_outbox();
1399
1400                 procmsg_save_to_outbox(outbox, file, TRUE);
1401         }
1402
1403         return (newsval != 0 ? newsval : mailval);
1404 }
1405
1406 #define CHANGE_FLAGS(msginfo) \
1407 { \
1408 if (msginfo->folder->folder->change_flags != NULL) \
1409 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
1410                                       msginfo->folder, \
1411                                       msginfo); \
1412 }
1413
1414 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1415 {
1416         gboolean changed = FALSE;
1417         FolderItem *item = msginfo->folder;
1418
1419         debug_print(_("Setting flags for message %d in folder %s\n"), msginfo->msgnum, item->path);
1420
1421         /* if new flag is set */
1422         if((perm_flags & MSG_NEW) && !MSG_IS_NEW(msginfo->flags) &&
1423            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1424                 item->new++;
1425                 changed = TRUE;
1426         }
1427
1428         /* if unread flag is set */
1429         if((perm_flags & MSG_UNREAD) && !MSG_IS_UNREAD(msginfo->flags) &&
1430            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1431                 item->unread++;
1432                 changed = TRUE;
1433         }
1434
1435         /* if ignore thread flag is set */
1436         if((perm_flags & MSG_IGNORE_THREAD) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1437                 if(MSG_IS_NEW(msginfo->flags) || (perm_flags & MSG_NEW)) {
1438                         item->new--;
1439                         changed = TRUE;
1440                 }
1441                 if(MSG_IS_UNREAD(msginfo->flags) || (perm_flags & MSG_UNREAD)) {
1442                         item->unread--;
1443                         changed = TRUE;
1444                 }
1445         }
1446
1447         if (MSG_IS_IMAP(msginfo->flags))
1448                 imap_msg_set_perm_flags(msginfo, perm_flags);
1449
1450         msginfo->flags.perm_flags |= perm_flags;
1451         msginfo->flags.tmp_flags |= tmp_flags;
1452
1453         if(changed) {
1454                 folderview_update_item(item, FALSE);
1455         }
1456         CHANGE_FLAGS(msginfo);
1457         procmsg_msginfo_write_flags(msginfo);
1458 }
1459
1460 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1461 {
1462         gboolean changed = FALSE;
1463         FolderItem *item = msginfo->folder;
1464         
1465         debug_print(_("Unsetting flags for message %d in folder %s\n"), msginfo->msgnum, item->path);
1466
1467         /* if new flag is unset */
1468         if((perm_flags & MSG_NEW) && MSG_IS_NEW(msginfo->flags) &&
1469            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1470                 item->new--;
1471                 changed = TRUE;
1472         }
1473
1474         /* if unread flag is unset */
1475         if((perm_flags & MSG_UNREAD) && MSG_IS_UNREAD(msginfo->flags) &&
1476            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1477                 item->unread--;
1478                 changed = TRUE;
1479         }
1480
1481         /* if ignore thread flag is unset */
1482         if((perm_flags & MSG_IGNORE_THREAD) && MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1483                 if(MSG_IS_NEW(msginfo->flags) && !(perm_flags & MSG_NEW)) {
1484                         item->new++;
1485                         changed = TRUE;
1486                 }
1487                 if(MSG_IS_UNREAD(msginfo->flags) && !(perm_flags & MSG_UNREAD)) {
1488                         item->unread++;
1489                         changed = TRUE;
1490                 }
1491         }
1492
1493         if (MSG_IS_IMAP(msginfo->flags))
1494                 imap_msg_unset_perm_flags(msginfo, perm_flags);
1495
1496         msginfo->flags.perm_flags &= ~perm_flags;
1497         msginfo->flags.tmp_flags &= ~tmp_flags;
1498
1499         if(changed) {
1500                 folderview_update_item(item, FALSE);
1501         }
1502         CHANGE_FLAGS(msginfo);
1503         procmsg_msginfo_write_flags(msginfo);
1504 }
1505
1506 void procmsg_msginfo_write_flags(MsgInfo *msginfo)
1507 {
1508         gchar *destdir;
1509         FILE *fp;
1510
1511         destdir = folder_item_get_path(msginfo->folder);
1512         if (!is_dir_exist(destdir))
1513                 make_dir_hier(destdir);
1514
1515         if ((fp = procmsg_open_mark_file(destdir, TRUE))) {
1516                 procmsg_write_flags(msginfo, fp);
1517                 fclose(fp);
1518         } else {
1519                 g_warning(_("Can't open mark file.\n"));
1520         }
1521         
1522         g_free(destdir);
1523 }