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