* src/folder.c
[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 #if 0 /* NEW CACHE DOES NOT ALLOW ACCESS TO THE MARK FILE */
483 void procmsg_get_mark_sum(const gchar *folder,
484                           gint *new, gint *unread, gint *total,
485                           gint *min, gint *max,
486                           gint first)
487 {
488         GHashTable *mark_table;
489         struct MarkSum marksum;
490
491         *new = *unread = *total = *min = *max = 0;
492         marksum.new    = new;
493         marksum.unread = unread;
494         marksum.total  = total;
495         marksum.min    = min;
496         marksum.max    = max;
497         marksum.first  = first;
498
499         mark_table = procmsg_read_mark_file(folder);
500
501         if (mark_table) {
502                 g_hash_table_foreach(mark_table, mark_sum_func, &marksum);
503                 g_hash_table_destroy(mark_table);
504         }
505         debug_print("mark->new = %d, mark->unread = %d, mark->total = %d\n",
506                     *(marksum.new), *(marksum.unread), *(marksum.total));
507 }
508 #endif
509
510 static GHashTable *procmsg_read_mark_file(const gchar *folder)
511 {
512         FILE *fp;
513         GHashTable *mark_table = NULL;
514         gint num;
515         MsgFlags *flags;
516         MsgPermFlags perm_flags;
517
518         if ((fp = procmsg_open_mark_file(folder, FALSE)) == NULL)
519                 return NULL;
520
521         mark_table = g_hash_table_new(NULL, g_direct_equal);
522
523         while (fread(&num, sizeof(num), 1, fp) == 1) {
524                 if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
525
526                 flags = g_new0(MsgFlags, 1);
527                 flags->perm_flags = perm_flags;
528     
529                 if(!MSG_IS_REALLY_DELETED(*flags)) {
530                         g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags);
531                 } else {
532                         g_hash_table_remove(mark_table, GUINT_TO_POINTER(num));
533                 }
534         }
535
536         fclose(fp);
537         return mark_table;
538 }
539
540 FILE *procmsg_open_mark_file(const gchar *folder, gboolean append)
541 {
542         gchar *markfile;
543         FILE *fp;
544         gint ver;
545
546         markfile = g_strconcat(folder, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
547
548         if ((fp = fopen(markfile, "rb")) == NULL)
549                 debug_print(_("Mark file not found.\n"));
550         else if (fread(&ver, sizeof(ver), 1, fp) != 1 || MARK_VERSION != ver) {
551                 debug_print(_("Mark version is different (%d != %d). "
552                               "Discarding it.\n"), ver, MARK_VERSION);
553                 fclose(fp);
554                 fp = NULL;
555         }
556
557         /* read mode */
558         if (append == FALSE) {
559                 g_free(markfile);
560                 return fp;
561         }
562
563         if (fp) {
564                 /* reopen with append mode */
565                 fclose(fp);
566                 if ((fp = fopen(markfile, "ab")) == NULL)
567                         g_warning(_("Can't open mark file with append mode.\n"));
568         } else {
569                 /* open with overwrite mode if mark file doesn't exist or
570                    version is different */
571                 if ((fp = fopen(markfile, "wb")) == NULL)
572                         g_warning(_("Can't open mark file with write mode.\n"));
573                 else {
574                         ver = MARK_VERSION;
575                         WRITE_CACHE_DATA_INT(ver, fp);
576                 }
577         }
578
579         g_free(markfile);
580         return fp;
581 }
582
583 /* return the reversed thread tree */
584 GNode *procmsg_get_thread_tree(GSList *mlist)
585 {
586         GNode *root, *parent, *node, *next;
587         GHashTable *msgid_table;
588         GHashTable *subject_table;
589         MsgInfo *msginfo;
590         const gchar *msgid;
591         const gchar *subject;
592
593         root = g_node_new(NULL);
594         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
595         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
596
597         for (; mlist != NULL; mlist = mlist->next) {
598                 msginfo = (MsgInfo *)mlist->data;
599                 parent = root;
600
601                 if (msginfo->inreplyto) {
602                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
603                         if (parent == NULL) {
604                                 parent = root;
605                         } else {
606                                 if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
607                                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
608                                 }
609                         }
610                 }
611                 node = g_node_insert_data_before
612                         (parent, parent == root ? parent->children : NULL,
613                          msginfo);
614                 if ((msgid = msginfo->msgid) &&
615                     g_hash_table_lookup(msgid_table, msgid) == NULL)
616                         g_hash_table_insert(msgid_table, (gchar *)msgid, node);
617
618                 subject = msginfo->subject;
619                 if (subject_table_lookup(subject_table,
620                                          (gchar *) subject) == NULL)
621                         subject_table_insert(subject_table, (gchar *)subject,
622                                              node);
623         }
624
625         /* complete the unfinished threads */
626         for (node = root->children; node != NULL; ) {
627                 next = node->next;
628                 msginfo = (MsgInfo *)node->data;
629                 parent = NULL;
630                 if (msginfo->inreplyto) 
631                         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
632                 if (parent && parent != node) {
633                         g_node_unlink(node);
634                         g_node_insert_before
635                                 (parent, parent->children, node);
636                         /* CLAWS: ignore thread */
637                         if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
638                                 procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
639                         }
640                 }
641                 node = next;
642         }
643
644         /* CLAWS: now see if the first level (below root) still has some nodes that can be
645          * threaded by subject line. we need to handle this in a special way to prevent
646          * circular reference from a node that has already been threaded by IN-REPLY-TO
647          * but is also in the subject line hash table */
648         for (node = root->children; node != NULL; ) {
649                 next = node->next;
650                 msginfo = (MsgInfo *) node->data;
651                 parent = NULL;
652                 if (subject_is_reply(msginfo->subject)) {
653                         parent = subject_table_lookup(subject_table,
654                                                       msginfo->subject);
655                         /* the node may already be threaded by IN-REPLY-TO,
656                            so go up in the tree to find the parent node */
657                         if (parent != NULL) {
658                                 if (g_node_is_ancestor(node, parent))
659                                         parent = NULL;
660                                 if (parent == node)
661                                         parent = NULL;
662                         }
663
664                         if (parent) {
665                                 g_node_unlink(node);
666                                 g_node_append(parent, node);
667                                 /* CLAWS: ignore thread */
668                                 if(MSG_IS_IGNORE_THREAD(((MsgInfo *)parent->data)->flags)) {
669                                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
670                                 }
671                         }
672                 }                                       
673                 node = next;
674         }               
675                 
676         g_hash_table_destroy(subject_table);
677         g_hash_table_destroy(msgid_table);
678
679         return root;
680 }
681
682 void procmsg_move_messages(GSList *mlist)
683 {
684         GSList *cur, *movelist = NULL;
685         MsgInfo *msginfo;
686         FolderItem *dest = NULL;
687         GHashTable *hash;
688
689         if (!mlist) return;
690
691         for (cur = mlist; cur != NULL; cur = cur->next) {
692                 msginfo = (MsgInfo *)cur->data;
693                 if (!dest) {
694                         dest = msginfo->to_folder;
695                         movelist = g_slist_append(movelist, msginfo);
696                 } else if (dest == msginfo->to_folder) {
697                         movelist = g_slist_append(movelist, msginfo);
698                 } else {
699                         folder_item_move_msgs_with_dest(dest, movelist);
700                         g_slist_free(movelist);
701                         movelist = NULL;
702                         dest = msginfo->to_folder;
703                         movelist = g_slist_append(movelist, msginfo);
704                 }
705         }
706
707         if (movelist) {
708                 folder_item_move_msgs_with_dest(dest, movelist);
709                 g_slist_free(movelist);
710         }
711 }
712
713 void procmsg_copy_messages(GSList *mlist)
714 {
715         GSList *cur, *copylist = NULL;
716         MsgInfo *msginfo;
717         FolderItem *dest = NULL;
718         GHashTable *hash;
719
720         if (!mlist) return;
721
722         /* 
723         
724         Horrible: Scanning 2 times for every copy!
725
726         hash = procmsg_to_folder_hash_table_create(mlist);
727         folder_item_scan_foreach(hash);
728         g_hash_table_destroy(hash);
729         */
730
731         for (cur = mlist; cur != NULL; cur = cur->next) {
732                 msginfo = (MsgInfo *)cur->data;
733                 if (!dest) {
734                         dest = msginfo->to_folder;
735                         copylist = g_slist_append(copylist, msginfo);
736                 } else if (dest == msginfo->to_folder) {
737                         copylist = g_slist_append(copylist, msginfo);
738                 } else {
739                         folder_item_copy_msgs_with_dest(dest, copylist);
740                         g_slist_free(copylist);
741                         copylist = NULL;
742                         dest = msginfo->to_folder;
743                         copylist = g_slist_append(copylist, msginfo);
744                 }
745         }
746
747         if (copylist) {
748                 folder_item_copy_msgs_with_dest(dest, copylist);
749                 g_slist_free(copylist);
750         }
751 }
752
753 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
754 {
755         gchar *path, *file;
756
757         g_return_val_if_fail(msginfo != NULL, NULL);
758
759         if (msginfo->plaintext_file)
760                 file = g_strdup(msginfo->plaintext_file);
761         else {
762                 path = folder_item_get_path(msginfo->folder);
763                 file = g_strconcat(path, G_DIR_SEPARATOR_S,
764                                    itos(msginfo->msgnum), NULL);
765                 g_free(path);
766         }
767
768         return file;
769 }
770
771 gchar *procmsg_get_message_file(MsgInfo *msginfo)
772 {
773         gchar *filename = NULL;
774
775         g_return_val_if_fail(msginfo != NULL, NULL);
776
777         filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
778         if (!filename)
779                 g_warning(_("can't fetch message %d\n"), msginfo->msgnum);
780
781         return filename;
782 }
783
784 FILE *procmsg_open_message(MsgInfo *msginfo)
785 {
786         FILE *fp;
787         gchar *file;
788
789         g_return_val_if_fail(msginfo != NULL, NULL);
790
791         file = procmsg_get_message_file_path(msginfo);
792         g_return_val_if_fail(file != NULL, NULL);
793
794         if (!is_file_exist(file)) {
795                 g_free(file);
796                 file = procmsg_get_message_file(msginfo);
797                 g_return_val_if_fail(file != NULL, NULL);
798         }
799
800         if ((fp = fopen(file, "rb")) == NULL) {
801                 FILE_OP_ERROR(file, "fopen");
802                 g_free(file);
803                 return NULL;
804         }
805
806         g_free(file);
807
808         if (MSG_IS_QUEUED(msginfo->flags)) {
809                 gchar buf[BUFFSIZE];
810
811                 while (fgets(buf, sizeof(buf), fp) != NULL)
812                         if (buf[0] == '\r' || buf[0] == '\n') break;
813         }
814
815         return fp;
816 }
817
818 #if USE_GPGME
819 FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo)
820 {
821         FILE *fp;
822         MimeInfo *mimeinfo_;
823
824         g_return_val_if_fail(msginfo != NULL, NULL);
825
826         if (mimeinfo) *mimeinfo = NULL;
827
828         if ((fp = procmsg_open_message(msginfo)) == NULL) return NULL;
829
830         mimeinfo_ = procmime_scan_mime_header(fp);
831         if (!mimeinfo_) {
832                 fclose(fp);
833                 return NULL;
834         }
835
836         if (!MSG_IS_ENCRYPTED(msginfo->flags) &&
837             rfc2015_is_encrypted(mimeinfo_)) {
838                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_ENCRYPTED);
839         }
840
841         if (MSG_IS_ENCRYPTED(msginfo->flags) &&
842             !msginfo->plaintext_file &&
843             !msginfo->decryption_failed) {
844                 rfc2015_decrypt_message(msginfo, mimeinfo_, fp);
845                 if (msginfo->plaintext_file &&
846                     !msginfo->decryption_failed) {
847                         fclose(fp);
848                         procmime_mimeinfo_free_all(mimeinfo_);
849                         if ((fp = procmsg_open_message(msginfo)) == NULL)
850                                 return NULL;
851                         mimeinfo_ = procmime_scan_mime_header(fp);
852                         if (!mimeinfo_) {
853                                 fclose(fp);
854                                 return NULL;
855                         }
856                 }
857         }
858
859         if (mimeinfo) *mimeinfo = mimeinfo_;
860         return fp;
861 }
862 #endif
863
864 gboolean procmsg_msg_exist(MsgInfo *msginfo)
865 {
866         gchar *path;
867         gboolean ret;
868
869         if (!msginfo) return FALSE;
870
871         path = folder_item_get_path(msginfo->folder);
872         change_dir(path);
873         ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
874         g_free(path);
875
876         return ret;
877 }
878
879 void procmsg_empty_trash(void)
880 {
881         FolderItem *trash;
882         GList *cur;
883
884         for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
885                 trash = FOLDER(cur->data)->trash;
886                 if (trash && trash->total > 0)
887                         folder_item_remove_all_msg(trash);
888         }
889 }
890
891 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
892 {
893         gint i;
894         gint ret = 0;
895         GSList *list, *elem;
896
897         if (!queue)
898                 queue = folder_get_default_queue();
899         g_return_val_if_fail(queue != NULL, -1);
900
901         folder_item_scan(queue);
902         list = folder_item_get_msg_list(queue);
903
904
905         for(elem = list; elem != NULL; elem = elem->next) {
906                 gchar *file;
907                 MsgInfo *msginfo;
908                 
909                 msginfo = (MsgInfo *)(elem->data);
910
911                 file = folder_item_fetch_msg(queue, msginfo->msgnum);
912                 if (file) {
913                         if (procmsg_send_message_queue(file) < 0) {
914                                 g_warning(_("Sending queued message %d failed.\n"), msginfo->msgnum);
915                                 ret = -1;
916                         } else {
917                         /* CLAWS: 
918                          * We save in procmsg_send_message_queue because
919                          * we need the destination folder from the queue
920                          * header
921                                                 
922                                 if (save_msgs)
923                                         procmsg_save_to_outbox
924                                                 (queue->folder->outbox,
925                                                  file, TRUE);
926 */
927                                 folder_item_remove_msg(queue, msginfo->msgnum);
928                         }
929                         g_free(file);
930                 }
931                 procmsg_msginfo_free(msginfo);
932         }
933
934         folderview_update_item(queue, FALSE);
935
936         return ret;
937 }
938
939 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
940                             gboolean is_queued)
941 {
942         gint num;
943         FILE *fp;
944         MsgFlags flag = {0, 0};
945         MsgInfo *msginfo;
946
947         debug_print(_("saving sent message...\n"));
948
949         if (!outbox)
950                 outbox = folder_get_default_outbox();
951         g_return_val_if_fail(outbox != NULL, -1);
952
953         /* remove queueing headers */
954         if (is_queued) {
955                 gchar tmp[MAXPATHLEN + 1];
956                 gchar buf[BUFFSIZE];
957                 FILE *outfp;
958
959                 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
960                            get_rc_dir(), G_DIR_SEPARATOR, (guint)random());
961                 if ((fp = fopen(file, "rb")) == NULL) {
962                         FILE_OP_ERROR(file, "fopen");
963                         return -1;
964                 }
965                 if ((outfp = fopen(tmp, "wb")) == NULL) {
966                         FILE_OP_ERROR(tmp, "fopen");
967                         fclose(fp);
968                         return -1;
969                 }
970                 while (fgets(buf, sizeof(buf), fp) != NULL)
971                         if (buf[0] == '\r' || buf[0] == '\n') break;
972                 while (fgets(buf, sizeof(buf), fp) != NULL)
973                         fputs(buf, outfp);
974                 fclose(outfp);
975                 fclose(fp);
976                 Xstrdup_a(file, tmp, return -1);
977         }
978
979         if ((num = folder_item_add_msg(outbox, file, FALSE)) < 0) {
980                 g_warning(_("can't save message\n"));
981                 if(is_queued) {
982                         unlink(file);
983                 }
984                 return -1;
985         }
986         msginfo = folder_item_fetch_msginfo(outbox, num);
987         procmsg_msginfo_unset_flags(msginfo, ~0, ~0);
988         procmsg_msginfo_free(msginfo);
989
990         if(is_queued) {
991                 unlink(file);
992         }
993
994         return 0;
995 }
996
997 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
998 {
999         static const gchar *def_cmd = "lpr %s";
1000         static guint id = 0;
1001         gchar *prtmp;
1002         FILE *tmpfp, *prfp;
1003         gchar buf[1024];
1004         gchar *p;
1005
1006         g_return_if_fail(msginfo);
1007
1008         if ((tmpfp = procmime_get_first_text_content(msginfo)) == NULL) {
1009                 g_warning(_("Can't get text part\n"));
1010                 return;
1011         }
1012
1013         prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1014                                 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1015
1016         if ((prfp = fopen(prtmp, "wb")) == NULL) {
1017                 FILE_OP_ERROR(prtmp, "fopen");
1018                 g_free(prtmp);
1019                 fclose(tmpfp);
1020                 return;
1021         }
1022
1023         if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1024         if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1025         if (msginfo->to)   fprintf(prfp, "To: %s\n", msginfo->to);
1026         if (msginfo->cc)   fprintf(prfp, "Cc: %s\n", msginfo->cc);
1027         if (msginfo->newsgroups)
1028                 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1029         if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1030         fputc('\n', prfp);
1031
1032         while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1033                 fputs(buf, prfp);
1034
1035         fclose(prfp);
1036         fclose(tmpfp);
1037
1038         if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1039             !strchr(p + 2, '%'))
1040                 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1041         else {
1042                 if (cmdline)
1043                         g_warning(_("Print command line is invalid: `%s'\n"),
1044                                   cmdline);
1045                 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1046         }
1047
1048         g_free(prtmp);
1049
1050         g_strchomp(buf);
1051         if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1052         system(buf);
1053 }
1054
1055 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1056 {
1057         msginfo->refcnt++;
1058         
1059         return msginfo;
1060 }
1061
1062 MsgInfo *procmsg_msginfo_new()
1063 {
1064         MsgInfo *newmsginfo;
1065
1066         newmsginfo = g_new0(MsgInfo, 1);
1067         newmsginfo->refcnt = 1;
1068         
1069         return newmsginfo;
1070 }
1071
1072 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1073 {
1074         MsgInfo *newmsginfo;
1075
1076         if (msginfo == NULL) return NULL;
1077
1078         newmsginfo = g_new0(MsgInfo, 1);
1079
1080         newmsginfo->refcnt = 1;
1081
1082 #define MEMBCOPY(mmb)   newmsginfo->mmb = msginfo->mmb
1083 #define MEMBDUP(mmb)    newmsginfo->mmb = msginfo->mmb ? \
1084                         g_strdup(msginfo->mmb) : NULL
1085
1086         MEMBCOPY(msgnum);
1087         MEMBCOPY(size);
1088         MEMBCOPY(mtime);
1089         MEMBCOPY(date_t);
1090         MEMBCOPY(flags);
1091
1092         MEMBDUP(fromname);
1093
1094         MEMBDUP(date);
1095         MEMBDUP(from);
1096         MEMBDUP(to);
1097         MEMBDUP(cc);
1098         MEMBDUP(newsgroups);
1099         MEMBDUP(subject);
1100         MEMBDUP(msgid);
1101         MEMBDUP(inreplyto);
1102         MEMBDUP(xref);
1103
1104         MEMBCOPY(folder);
1105         MEMBCOPY(to_folder);
1106
1107         MEMBDUP(xface);
1108         MEMBDUP(dispositionnotificationto);
1109         MEMBDUP(returnreceiptto);
1110         MEMBDUP(references);
1111
1112         MEMBCOPY(score);
1113         MEMBCOPY(threadscore);
1114
1115         return newmsginfo;
1116 }
1117
1118 void procmsg_msginfo_free(MsgInfo *msginfo)
1119 {
1120         if (msginfo == NULL) return;
1121
1122         msginfo->refcnt--;
1123         if(msginfo->refcnt > 0)
1124                 return;
1125
1126         g_free(msginfo->fromspace);
1127         g_free(msginfo->references);
1128         g_free(msginfo->returnreceiptto);
1129         g_free(msginfo->dispositionnotificationto);
1130         g_free(msginfo->xface);
1131
1132         g_free(msginfo->fromname);
1133
1134         g_free(msginfo->date);
1135         g_free(msginfo->from);
1136         g_free(msginfo->to);
1137         g_free(msginfo->cc);
1138         g_free(msginfo->newsgroups);
1139         g_free(msginfo->subject);
1140         g_free(msginfo->msgid);
1141         g_free(msginfo->inreplyto);
1142         g_free(msginfo->xref);
1143
1144         g_free(msginfo);
1145 }
1146
1147 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1148 {
1149         guint memusage = 0;
1150         
1151         memusage += sizeof(MsgInfo);
1152         if(msginfo->fromname)
1153                 memusage += strlen(msginfo->fromname);
1154         if(msginfo->date)
1155                 memusage += strlen(msginfo->date);
1156         if(msginfo->from)
1157                 memusage += strlen(msginfo->from);
1158         if(msginfo->to)
1159                 memusage += strlen(msginfo->to);
1160         if(msginfo->cc)
1161                 memusage += strlen(msginfo->cc);
1162         if(msginfo->newsgroups)
1163                 memusage += strlen(msginfo->newsgroups);
1164         if(msginfo->subject)
1165                 memusage += strlen(msginfo->subject);
1166         if(msginfo->msgid)
1167                 memusage += strlen(msginfo->msgid);
1168         if(msginfo->inreplyto)
1169                 memusage += strlen(msginfo->inreplyto);
1170         if(msginfo->xface)
1171                 memusage += strlen(msginfo->xface);
1172         if(msginfo->dispositionnotificationto)
1173                 memusage += strlen(msginfo->dispositionnotificationto);
1174         if(msginfo->returnreceiptto)
1175                 memusage += strlen(msginfo->returnreceiptto);
1176         if(msginfo->references)
1177                 memusage += strlen(msginfo->references);
1178         if(msginfo->fromspace)
1179                 memusage += strlen(msginfo->fromspace);
1180
1181         return memusage;
1182 }
1183
1184 static gint procmsg_cmp_msgnum(gconstpointer a, gconstpointer b)
1185 {
1186         const MsgInfo *msginfo = a;
1187         const guint msgnum = GPOINTER_TO_UINT(b);
1188
1189         if (!msginfo)
1190                 return -1;
1191
1192         return msginfo->msgnum - msgnum;
1193 }
1194
1195 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1196 {
1197         const MsgInfo *msginfo1 = a;
1198         const MsgInfo *msginfo2 = b;
1199
1200         if (!msginfo1)
1201                 return -1;
1202         if (!msginfo2)
1203                 return -1;
1204
1205         return msginfo1->msgnum - msginfo2->msgnum;
1206 }
1207
1208 static gint procmsg_cmp_flag_msgnum(gconstpointer a, gconstpointer b)
1209 {
1210         const FlagInfo *finfo = a;
1211         const guint msgnum = GPOINTER_TO_UINT(b);
1212
1213         if (!finfo)
1214                 return -1;
1215
1216         return finfo->msgnum - msgnum;
1217 }
1218
1219 enum
1220 {
1221         Q_SENDER           = 0,
1222         Q_SMTPSERVER       = 1,
1223         Q_RECIPIENTS       = 2,
1224         Q_NEWSGROUPS       = 3,
1225         Q_MAIL_ACCOUNT_ID  = 4,
1226         Q_NEWS_ACCOUNT_ID  = 5,
1227         Q_SAVE_COPY_FOLDER = 6
1228 };
1229
1230 gint procmsg_send_message_queue(const gchar *file)
1231 {
1232         static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
1233                                        {"SSV:",  NULL, FALSE},
1234                                        {"R:",    NULL, FALSE},
1235                                        {"NG:",   NULL, FALSE},
1236                                        {"MAID:", NULL, FALSE},
1237                                        {"NAID:", NULL, FALSE},
1238                                        {"SCF:",  NULL, FALSE},
1239                                        {NULL,    NULL, FALSE}};
1240         FILE *fp;
1241         gint filepos;
1242         gint mailval = 0, newsval = 0;
1243         gchar *from = NULL;
1244         gchar *smtpserver = NULL;
1245         GSList *to_list = NULL;
1246         GSList *newsgroup_list = NULL;
1247         gchar *savecopyfolder = NULL;
1248         gchar buf[BUFFSIZE];
1249         gint hnum;
1250         PrefsAccount *mailac = NULL, *newsac = NULL;
1251         int local = 0;
1252
1253         g_return_val_if_fail(file != NULL, -1);
1254
1255         if ((fp = fopen(file, "rb")) == NULL) {
1256                 FILE_OP_ERROR(file, "fopen");
1257                 return -1;
1258         }
1259
1260         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1261                != -1) {
1262                 gchar *p = buf + strlen(qentry[hnum].name);
1263
1264                 switch (hnum) {
1265                 case Q_SENDER:
1266                         if (!from) from = g_strdup(p);
1267                         break;
1268                 case Q_SMTPSERVER:
1269                         if (!smtpserver) smtpserver = g_strdup(p);
1270                         break;
1271                 case Q_RECIPIENTS:
1272                         to_list = address_list_append(to_list, p);
1273                         break;
1274                 case Q_NEWSGROUPS:
1275                         newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1276                         break;
1277                 case Q_MAIL_ACCOUNT_ID:
1278                         mailac = account_find_from_id(atoi(p));
1279                         break;
1280                 case Q_NEWS_ACCOUNT_ID:
1281                         newsac = account_find_from_id(atoi(p));
1282                         break;
1283                 case Q_SAVE_COPY_FOLDER:
1284                         if (!savecopyfolder) savecopyfolder = g_strdup(p);
1285                         break;
1286                 }
1287         }
1288         filepos = ftell(fp);
1289
1290         fseek(fp, filepos, SEEK_SET);
1291         if (to_list) {
1292                 debug_print(_("Sending message by mail\n"));
1293                 if(!from) {
1294                         g_warning(_("Queued message header is broken.\n"));
1295                         mailval = -1;
1296                 } else if (mailac && mailac->use_mail_command &&
1297                            mailac->mail_command && (* mailac->mail_command)) {
1298                         mailval = send_message_local(mailac->mail_command, fp);
1299                         local = 1;
1300                 } else if (prefs_common.use_extsend && prefs_common.extsend_cmd) {
1301                         mailval = send_message_local(prefs_common.extsend_cmd, fp);
1302                         local = 1;
1303                 } else {
1304                         if (!mailac) {
1305                                 mailac = account_find_from_smtp_server(from, smtpserver);
1306                                 if (!mailac) {
1307                                         g_warning(_("Account not found. "
1308                                                     "Using current account...\n"));
1309                                         mailac = cur_account;
1310                                 }
1311                         }
1312
1313                         if (mailac)
1314                                 mailval = send_message_smtp(mailac, to_list, fp);
1315                         else {
1316                                 PrefsAccount tmp_ac;
1317
1318                                 g_warning(_("Account not found.\n"));
1319
1320                                 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1321                                 tmp_ac.address = from;
1322                                 tmp_ac.smtp_server = smtpserver;
1323                                 tmp_ac.smtpport = SMTP_PORT;
1324                                 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1325                         }
1326                 }
1327                 if (mailval < 0) {
1328                         if (!local)
1329                                 alertpanel_error(
1330                                         _("Error occurred while sending the message to `%s'."),
1331                                         mailac ? mailac->smtp_server : smtpserver);
1332                         else
1333                                 alertpanel_error(
1334                                         _("Error occurred while sending the message with command `%s'."),
1335                                         (mailac && mailac->use_mail_command && 
1336                                          mailac->mail_command && (*mailac->mail_command)) ? 
1337                                                 mailac->mail_command : prefs_common.extsend_cmd);
1338                 }
1339         }
1340
1341         if(newsgroup_list && (newsval == 0)) {
1342                 Folder *folder;
1343                 gchar *tmp = NULL;
1344                 FILE *tmpfp;
1345
1346                 /* write to temporary file */
1347                 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1348                             G_DIR_SEPARATOR, (gint)file);
1349                 if ((tmpfp = fopen(tmp, "wb")) == NULL) {
1350                         FILE_OP_ERROR(tmp, "fopen");
1351                         newsval = -1;
1352                         alertpanel_error(_("Could not create temporary file for news sending."));
1353                 } else {
1354                         if (change_file_mode_rw(tmpfp, tmp) < 0) {
1355                                 FILE_OP_ERROR(tmp, "chmod");
1356                                 g_warning(_("can't change file mode\n"));
1357                         }
1358
1359                         while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1360                                 if (fputs(buf, tmpfp) == EOF) {
1361                                         FILE_OP_ERROR(tmp, "fputs");
1362                                         newsval = -1;
1363                                         alertpanel_error(_("Error when writing temporary file for news sending."));
1364                                 }
1365                         }
1366                         fclose(tmpfp);
1367
1368                         if(newsval == 0) {
1369                                 debug_print(_("Sending message by news\n"));
1370
1371                                 folder = FOLDER(newsac->folder);
1372
1373                                 newsval = news_post(folder, tmp);
1374                                 if (newsval < 0) {
1375                                         alertpanel_error(_("Error occurred while posting the message to %s ."),
1376                                                  newsac->nntp_server);
1377                                 }
1378                         }
1379                         unlink(tmp);
1380                 }
1381                 g_free(tmp);
1382         }
1383
1384         slist_free_strings(to_list);
1385         g_slist_free(to_list);
1386         slist_free_strings(newsgroup_list);
1387         g_slist_free(newsgroup_list);
1388         g_free(from);
1389         g_free(smtpserver);
1390         fclose(fp);
1391
1392         /* save message to outbox */
1393         if (mailval == 0 && newsval == 0 && savecopyfolder) {
1394                 FolderItem *outbox;
1395
1396                 debug_print(_("saving sent message...\n"));
1397
1398                 outbox = folder_find_item_from_identifier(savecopyfolder);
1399                 if(!outbox)
1400                         outbox = folder_get_default_outbox();
1401
1402                 procmsg_save_to_outbox(outbox, file, TRUE);
1403         }
1404
1405         return (newsval != 0 ? newsval : mailval);
1406 }
1407
1408 #define CHANGE_FLAGS(msginfo) \
1409 { \
1410 if (msginfo->folder->folder->change_flags != NULL) \
1411 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
1412                                       msginfo->folder, \
1413                                       msginfo); \
1414 }
1415
1416 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1417 {
1418         gboolean changed = FALSE;
1419         FolderItem *item = msginfo->folder;
1420
1421         debug_print(_("Setting flags for message %d in folder %s\n"), msginfo->msgnum, item->path);
1422
1423         /* if new flag is set */
1424         if((perm_flags & MSG_NEW) && !MSG_IS_NEW(msginfo->flags) &&
1425            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1426                 item->new++;
1427                 changed = TRUE;
1428         }
1429
1430         /* if unread flag is set */
1431         if((perm_flags & MSG_UNREAD) && !MSG_IS_UNREAD(msginfo->flags) &&
1432            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1433                 item->unread++;
1434                 changed = TRUE;
1435         }
1436
1437         /* if ignore thread flag is set */
1438         if((perm_flags & MSG_IGNORE_THREAD) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1439                 if(MSG_IS_NEW(msginfo->flags) || (perm_flags & MSG_NEW)) {
1440                         item->new--;
1441                         changed = TRUE;
1442                 }
1443                 if(MSG_IS_UNREAD(msginfo->flags) || (perm_flags & MSG_UNREAD)) {
1444                         item->unread--;
1445                         changed = TRUE;
1446                 }
1447         }
1448
1449         if (MSG_IS_IMAP(msginfo->flags))
1450                 imap_msg_set_perm_flags(msginfo, perm_flags);
1451
1452         msginfo->flags.perm_flags |= perm_flags;
1453         msginfo->flags.tmp_flags |= tmp_flags;
1454
1455         if(changed) {
1456                 folderview_update_item(item, FALSE);
1457         }
1458         CHANGE_FLAGS(msginfo);
1459         procmsg_msginfo_write_flags(msginfo);
1460 }
1461
1462 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1463 {
1464         gboolean changed = FALSE;
1465         FolderItem *item = msginfo->folder;
1466         
1467         debug_print(_("Unsetting flags for message %d in folder %s\n"), msginfo->msgnum, item->path);
1468
1469         /* if new flag is unset */
1470         if((perm_flags & MSG_NEW) && MSG_IS_NEW(msginfo->flags) &&
1471            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1472                 item->new--;
1473                 changed = TRUE;
1474         }
1475
1476         /* if unread flag is unset */
1477         if((perm_flags & MSG_UNREAD) && MSG_IS_UNREAD(msginfo->flags) &&
1478            !MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1479                 item->unread--;
1480                 changed = TRUE;
1481         }
1482
1483         /* if ignore thread flag is unset */
1484         if((perm_flags & MSG_IGNORE_THREAD) && MSG_IS_IGNORE_THREAD(msginfo->flags)) {
1485                 if(MSG_IS_NEW(msginfo->flags) && !(perm_flags & MSG_NEW)) {
1486                         item->new++;
1487                         changed = TRUE;
1488                 }
1489                 if(MSG_IS_UNREAD(msginfo->flags) && !(perm_flags & MSG_UNREAD)) {
1490                         item->unread++;
1491                         changed = TRUE;
1492                 }
1493         }
1494
1495         if (MSG_IS_IMAP(msginfo->flags))
1496                 imap_msg_unset_perm_flags(msginfo, perm_flags);
1497
1498         msginfo->flags.perm_flags &= ~perm_flags;
1499         msginfo->flags.tmp_flags &= ~tmp_flags;
1500
1501         if(changed) {
1502                 folderview_update_item(item, FALSE);
1503         }
1504         CHANGE_FLAGS(msginfo);
1505         procmsg_msginfo_write_flags(msginfo);
1506 }
1507
1508 void procmsg_msginfo_write_flags(MsgInfo *msginfo)
1509 {
1510         gchar *destdir;
1511         FILE *fp;
1512
1513         destdir = folder_item_get_path(msginfo->folder);
1514         if (!is_dir_exist(destdir))
1515                 make_dir_hier(destdir);
1516
1517         if ((fp = procmsg_open_mark_file(destdir, TRUE))) {
1518                 procmsg_write_flags(msginfo, fp);
1519                 fclose(fp);
1520         } else {
1521                 g_warning(_("Can't open mark file.\n"));
1522         }
1523         
1524         g_free(destdir);
1525 }