talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

procmsg.c (52455B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail team and 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 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <sys/param.h>
     26 #include <ctype.h>
     27 #include <math.h>
     28 
     29 
     30 #include "main.h"
     31 #include "utils.h"
     32 #include "procmsg.h"
     33 #include "procheader.h"
     34 #include "send_message.h"
     35 #include "procmime.h"
     36 #include "statusbar.h"
     37 #include "fence.h"
     38 #include "folder.h"
     39 #include "foldersel.h"
     40 #include "prefs_common.h"
     41 #include "account.h"
     42 #include "alertpanel.h"
     43 #include "hooks.h"
     44 #include "msgcache.h"
     45 #include "mainwindow.h"
     46 #include "summaryview.h"
     47 #include "log.h"
     48 #include "inc.h"
     49 #include "file-utils.h"
     50 
     51 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
     52 					    FolderItem *queue, gint msgnum, gboolean *queued_removed);
     53 static void procmsg_update_unread_children	(MsgInfo 	*info,
     54 					 gboolean 	 newly_marked);
     55 enum
     56 {
     57 	Q_SENDER           = 0,
     58 	Q_SMTPSERVER       = 1,
     59 	Q_RECIPIENTS       = 2,
     60 	Q_NEWSGROUPS       = 3,
     61 	Q_MAIL_ACCOUNT_ID  = 4,
     62 	Q_NEWS_ACCOUNT_ID  = 5,
     63 	Q_SAVE_COPY_FOLDER = 6,
     64 	Q_REPLY_MESSAGE_ID = 7,
     65 	Q_FWD_MESSAGE_ID   = 8,
     66 	Q_PRIVACY_SYSTEM   = 9,
     67 	Q_ENCRYPT 	   = 10,
     68 	Q_ENCRYPT_DATA	   = 11,
     69 	Q_CLAWS_HDRS       = 12,
     70 	Q_PRIVACY_SYSTEM_OLD = 13,
     71 	Q_ENCRYPT_OLD 	     = 14,
     72 	Q_ENCRYPT_DATA_OLD   = 15,
     73 	Q_CLAWS_HDRS_OLD     = 16,
     74 };
     75 
     76 void procmsg_msg_list_free(GSList *mlist)
     77 {
     78 	GSList *cur;
     79 	MsgInfo *msginfo;
     80 
     81 	for (cur = mlist; cur != NULL; cur = cur->next) {
     82 		msginfo = (MsgInfo *)cur->data;
     83 		procmsg_msginfo_free(&msginfo);
     84 	}
     85 	g_slist_free(mlist);
     86 }
     87 
     88 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
     89 {
     90 	GSList *cur = NULL;
     91 	GSList *nums = NULL;
     92 
     93 	for (cur = msglist; cur; cur = cur->next) {
     94 		MsgInfo *msg = (MsgInfo *)cur->data;
     95 		nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
     96 	}
     97 
     98 	return g_slist_reverse(nums);
     99 }
    100 
    101 struct MarkSum {
    102 	gint *new_msgs;
    103 	gint *unread_msgs;
    104 	gint *total_msgs;
    105 	gint *min;
    106 	gint *max;
    107 	gint first;
    108 };
    109 
    110 /* CLAWS subject threading:
    111 
    112   in the first round it inserts subject lines in a
    113   hashtable (subject <-> node)
    114 
    115   the second round finishes the threads by attaching
    116   matching subject lines to the one found in the
    117   hashtable. will use the oldest node with the same
    118   subject that is not more then thread_by_subject_max_age
    119   days old (see subject_hashtable_lookup)
    120 */
    121 
    122 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
    123 {
    124 	gchar *subject;
    125 	MsgInfo *msginfo;
    126 	GSList *list = NULL;
    127 
    128 	cm_return_if_fail(hashtable != NULL);
    129 	cm_return_if_fail(node != NULL);
    130 	msginfo = (MsgInfo *) node->data;
    131 	cm_return_if_fail(msginfo != NULL);
    132 
    133 	subject = msginfo->subject;
    134 	if (subject == NULL)
    135 		return;
    136 
    137 	subject += subject_get_prefix_length(subject);
    138 
    139 	list = g_hash_table_lookup(hashtable, subject);
    140 	list = g_slist_prepend(list, node);
    141 	g_hash_table_insert(hashtable, subject, list);
    142 }
    143 
    144 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
    145 {
    146 	gchar *subject;
    147 	GSList *list, *cur;
    148 	GNode *node = NULL, *hashtable_node = NULL;
    149 	gint prefix_length;
    150 	MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
    151 	gboolean match;
    152 
    153 	cm_return_val_if_fail(hashtable != NULL, NULL);
    154 
    155 	subject = msginfo->subject;
    156 	if (subject == NULL)
    157 		return NULL;
    158 	prefix_length = subject_get_prefix_length(subject);
    159 	if (prefix_length <= 0)
    160 		return NULL;
    161 	subject += prefix_length;
    162 
    163 	list = g_hash_table_lookup(hashtable, subject);
    164 	if (list == NULL)
    165 		return NULL;
    166 
    167 	/* check all nodes with the same subject to find the best parent */
    168 	for (cur = list; cur; cur = cur->next) {
    169 		hashtable_node = (GNode *)cur->data;
    170 		hashtable_msginfo = (MsgInfo *) hashtable_node->data;
    171 		match = FALSE;
    172 
    173 		/* best node should be the oldest in the found nodes */
    174 		/* parent node must not be older then msginfo */
    175 		if ((hashtable_msginfo->date_t < msginfo->date_t) &&
    176 		    ((best_msginfo == NULL) ||
    177 		     (best_msginfo->date_t > hashtable_msginfo->date_t)))
    178 			match = TRUE;
    179 
    180 		/* parent node must not be more then thread_by_subject_max_age
    181 		   days older then msginfo */
    182 		if (fabs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
    183                     prefs_common.thread_by_subject_max_age * 3600 * 24)
    184 			match = FALSE;
    185 
    186 		/* can add new tests for all matching
    187 		   nodes found by subject */
    188 
    189 		if (match) {
    190 			node = hashtable_node;
    191 			best_msginfo = hashtable_msginfo;
    192 		}
    193 	}
    194 
    195 	return node;
    196 }
    197 
    198 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
    199 {
    200 	g_slist_free(value);
    201 }
    202 
    203 /* return the reversed thread tree */
    204 GNode *procmsg_get_thread_tree(GSList *mlist)
    205 {
    206 	GNode *root, *parent, *node, *next;
    207 	GHashTable *msgid_table;
    208 	GHashTable *subject_hashtable = NULL;
    209 	MsgInfo *msginfo;
    210 	const gchar *msgid;
    211         GSList *reflist;
    212 	root = g_node_new(NULL);
    213 	msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
    214 
    215 	if (prefs_common.thread_by_subject) {
    216 		subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
    217 	}
    218 
    219 	for (; mlist != NULL; mlist = mlist->next) {
    220 		msginfo = (MsgInfo *)mlist->data;
    221 		parent = root;
    222 
    223 		if (msginfo->inreplyto) {
    224 			parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
    225 			if (parent == NULL) {
    226 				parent = root;
    227 			}
    228 		}
    229 		node = g_node_insert_data_before
    230 			(parent, parent == root ? parent->children : NULL,
    231 			 msginfo);
    232 		if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
    233 			g_hash_table_insert(msgid_table, (gchar *)msgid, node);
    234 
    235 		/* CLAWS: add subject to hashtable (without prefix) */
    236 		if (prefs_common.thread_by_subject) {
    237 			subject_hashtable_insert(subject_hashtable, node);
    238 		}
    239 	}
    240 
    241 	/* complete the unfinished threads */
    242 	for (node = root->children; node != NULL; ) {
    243 		next = node->next;
    244 		msginfo = (MsgInfo *)node->data;
    245 		parent = NULL;
    246 
    247                 if (msginfo->inreplyto)
    248 			parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
    249 
    250 		/* try looking for the indirect parent */
    251 		if (!parent && msginfo->references) {
    252 			for (reflist = msginfo->references;
    253 			     reflist != NULL; reflist = reflist->next)
    254 				if ((parent = g_hash_table_lookup
    255 					(msgid_table, reflist->data)) != NULL)
    256 					break;
    257                 }
    258 
    259 		/* node should not be the parent, and node should not
    260 		   be an ancestor of parent (circular reference) */
    261 		if (parent && parent != node &&
    262 		    !g_node_is_ancestor(node, parent)) {
    263 			g_node_unlink(node);
    264 			g_node_insert_before
    265 				(parent, parent->children, node);
    266 		}
    267 
    268 		node = next;
    269 	}
    270 
    271 	if (prefs_common.thread_by_subject) {
    272 		for (node = root->children; node && node != NULL;) {
    273 			next = node->next;
    274 			msginfo = (MsgInfo *) node->data;
    275 
    276 			parent = subject_hashtable_lookup(subject_hashtable, msginfo);
    277 
    278 			/* the node may already be threaded by IN-REPLY-TO, so go up
    279 			 * in the tree to
    280 			   find the parent node */
    281 			if (parent != NULL) {
    282 				if (g_node_is_ancestor(node, parent))
    283 					parent = NULL;
    284 				if (parent == node)
    285 					parent = NULL;
    286 			}
    287 
    288 			if (parent) {
    289 				g_node_unlink(node);
    290 				g_node_append(parent, node);
    291 			}
    292 
    293 			node = next;
    294 		}
    295 	}
    296 
    297 	if (prefs_common.thread_by_subject)
    298 	{
    299 		g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
    300 		g_hash_table_destroy(subject_hashtable);
    301 	}
    302 
    303 	g_hash_table_destroy(msgid_table);
    304 	return root;
    305 }
    306 
    307 gint procmsg_move_messages(GSList *mlist)
    308 {
    309 	GSList *cur, *movelist = NULL;
    310 	MsgInfo *msginfo;
    311 	FolderItem *dest = NULL;
    312 	gint retval = 0;
    313 	gboolean finished = TRUE;
    314 	if (!mlist) return 0;
    315 
    316 	folder_item_update_freeze();
    317 
    318 next_folder:
    319 	for (cur = mlist; cur != NULL; cur = cur->next) {
    320 		msginfo = (MsgInfo *)cur->data;
    321 		if (!msginfo->to_folder) {
    322 			continue;
    323 		} else {
    324 			finished = FALSE;
    325 		}
    326 		if (!dest) {
    327 			dest = msginfo->to_folder;
    328 			movelist = g_slist_prepend(movelist, msginfo);
    329 		} else if (dest == msginfo->to_folder) {
    330 			movelist = g_slist_prepend(movelist, msginfo);
    331 		} else {
    332 			continue;
    333 		}
    334 		procmsg_msginfo_set_to_folder(msginfo, NULL);
    335 	}
    336 	if (movelist) {
    337 		movelist = g_slist_reverse(movelist);
    338 		retval |= folder_item_move_msgs(dest, movelist);
    339 		g_slist_free(movelist);
    340 		movelist = NULL;
    341 	}
    342 	if (finished == FALSE) {
    343 		finished = TRUE;
    344 		dest = NULL;
    345 		goto next_folder;
    346 	}
    347 
    348 	folder_item_update_thaw();
    349 	return retval;
    350 }
    351 
    352 void procmsg_copy_messages(GSList *mlist)
    353 {
    354 	GSList *cur, *copylist = NULL;
    355 	MsgInfo *msginfo;
    356 	FolderItem *dest = NULL;
    357 	gboolean finished = TRUE;
    358 	if (!mlist) return;
    359 
    360 	folder_item_update_freeze();
    361 
    362 next_folder:
    363 	for (cur = mlist; cur != NULL; cur = cur->next) {
    364 		msginfo = (MsgInfo *)cur->data;
    365 		if (!msginfo->to_folder) {
    366 			continue;
    367 		} else {
    368 			finished = FALSE;
    369 		}
    370 		if (!dest) {
    371 			dest = msginfo->to_folder;
    372 			copylist = g_slist_prepend(copylist, msginfo);
    373 		} else if (dest == msginfo->to_folder) {
    374 			copylist = g_slist_prepend(copylist, msginfo);
    375 		} else {
    376 			continue;
    377 		}
    378 		procmsg_msginfo_set_to_folder(msginfo, NULL);
    379 	}
    380 	if (copylist) {
    381 		copylist = g_slist_reverse(copylist);
    382 		folder_item_copy_msgs(dest, copylist);
    383 		g_slist_free(copylist);
    384 		copylist = NULL;
    385 	}
    386 	if (finished == FALSE) {
    387 		finished = TRUE;
    388 		dest = NULL;
    389 		goto next_folder;
    390 	}
    391 
    392 	folder_item_update_thaw();
    393 }
    394 
    395 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
    396 {
    397 	cm_return_val_if_fail(msginfo != NULL, NULL);
    398 
    399 	return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
    400 }
    401 
    402 gchar *procmsg_get_message_file(MsgInfo *msginfo)
    403 {
    404 	gchar *filename = NULL;
    405 
    406 	cm_return_val_if_fail(msginfo != NULL, NULL);
    407 
    408 	filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
    409 	if (!filename)
    410 		debug_print("can't fetch message %d\n", msginfo->msgnum);
    411 
    412 	return filename;
    413 }
    414 
    415 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
    416 {
    417 	gchar *filename = NULL;
    418 
    419 	cm_return_val_if_fail(msginfo != NULL, NULL);
    420 
    421 	filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
    422 						headers, body);
    423 	if (!filename)
    424 		debug_print("can't fetch message %d\n", msginfo->msgnum);
    425 
    426 	return filename;
    427 }
    428 
    429 GSList *procmsg_get_message_file_list(GSList *mlist)
    430 {
    431         GSList *file_list = NULL;
    432         MsgInfo *msginfo;
    433         MsgFileInfo *fileinfo;
    434         gchar *file;
    435 
    436         while (mlist != NULL) {
    437                 msginfo = (MsgInfo *)mlist->data;
    438                 file = procmsg_get_message_file(msginfo);
    439                 if (!file) {
    440                         procmsg_message_file_list_free(file_list);
    441                         return NULL;
    442                 }
    443                 fileinfo = g_new(MsgFileInfo, 1);
    444 		fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
    445                 fileinfo->file = file;
    446                 fileinfo->flags = g_new(MsgFlags, 1);
    447                 *fileinfo->flags = msginfo->flags;
    448                 file_list = g_slist_prepend(file_list, fileinfo);
    449                 mlist = mlist->next;
    450         }
    451 
    452         file_list = g_slist_reverse(file_list);
    453 
    454         return file_list;
    455 }
    456 
    457 void procmsg_message_file_list_free(MsgInfoList *file_list)
    458 {
    459 	GSList *cur;
    460 	MsgFileInfo *fileinfo;
    461 
    462 	for (cur = file_list; cur != NULL; cur = cur->next) {
    463 		fileinfo = (MsgFileInfo *)cur->data;
    464 		procmsg_msginfo_free(&(fileinfo->msginfo));
    465 		g_free(fileinfo->file);
    466 		g_free(fileinfo->flags);
    467 		g_free(fileinfo);
    468 	}
    469 
    470 	g_slist_free(file_list);
    471 }
    472 
    473 FILE *procmsg_open_message(MsgInfo *msginfo, gboolean skip_special_headers)
    474 {
    475 	FILE *fp;
    476 	gchar *file;
    477 
    478 	cm_return_val_if_fail(msginfo != NULL, NULL);
    479 
    480 	file = procmsg_get_message_file_path(msginfo);
    481 	cm_return_val_if_fail(file != NULL, NULL);
    482 
    483 	if (!is_file_exist(file)) {
    484 		g_free(file);
    485 		file = procmsg_get_message_file(msginfo);
    486 		if (!file)
    487 			return NULL;
    488 	}
    489 
    490 	if ((fp = g_fopen(file, "rb")) == NULL) {
    491 		FILE_OP_ERROR(file, "g_fopen");
    492 		g_free(file);
    493 		return NULL;
    494 	}
    495 
    496 	g_free(file);
    497 
    498 	if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags) ||
    499 	    skip_special_headers == TRUE) {
    500 		gchar buf[BUFFSIZE];
    501 
    502 		while (fgets(buf, sizeof(buf), fp) != NULL) {
    503 			/* new way */
    504 			if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
    505 				strlen("X-Claws-End-Special-Headers:"))) ||
    506 			    (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
    507 				strlen("X-Sylpheed-End-Special-Headers:"))))
    508 				break;
    509 			/* old way */
    510 			if (buf[0] == '\r' || buf[0] == '\n') break;
    511 			/* from other mailers */
    512 			if (!strncmp(buf, "Date: ", 6)
    513 			||  !strncmp(buf, "To: ", 4)
    514 			||  !strncmp(buf, "From: ", 6)
    515 			||  !strncmp(buf, "Subject: ", 9)) {
    516 				rewind(fp);
    517 				break;
    518 			}
    519 		}
    520 	}
    521 
    522 	return fp;
    523 }
    524 
    525 gboolean procmsg_msg_exist(MsgInfo *msginfo)
    526 {
    527 	gboolean ret;
    528 
    529 	if (!msginfo) return FALSE;
    530 
    531 	ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
    532 
    533 	return ret;
    534 }
    535 
    536 static void procmsg_empty_trash(FolderItem *trash)
    537 {
    538 	GNode *node, *next;
    539 
    540 	if (!trash ||
    541 	    (trash->stype != F_TRASH &&
    542 	     !folder_has_parent_of_type(trash, F_TRASH)))
    543 		return;
    544 
    545 	if (trash && trash->total_msgs > 0) {
    546 		GSList *mlist = folder_item_get_msg_list(trash);
    547 		GSList *cur;
    548 		for (cur = mlist ; cur != NULL ; cur = cur->next) {
    549 			MsgInfo * msginfo = (MsgInfo *) cur->data;
    550 			if (MSG_IS_LOCKED(msginfo->flags)) {
    551 				procmsg_msginfo_free(&msginfo);
    552 				continue;
    553 			}
    554 			procmsg_msginfo_free(&msginfo);
    555 		}
    556 		g_slist_free(mlist);
    557 		folder_item_remove_all_msg(trash);
    558 	}
    559 
    560 	if (!trash->node || !trash->node->children)
    561 		return;
    562 
    563 	node = trash->node->children;
    564 	while (node != NULL) {
    565 		next = node->next;
    566 		procmsg_empty_trash(FOLDER_ITEM(node->data));
    567 		node = next;
    568 	}
    569 }
    570 
    571 void procmsg_empty_all_trash(void)
    572 {
    573 	FolderItem *trash;
    574 	GList *cur;
    575 
    576 	for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
    577 		Folder *folder = FOLDER(cur->data);
    578 		trash = folder->trash;
    579 		procmsg_empty_trash(trash);
    580 		if (folder->account && folder->account->set_trash_folder &&
    581 		    folder_find_item_from_identifier(folder->account->trash_folder))
    582 		    	procmsg_empty_trash(
    583 				folder_find_item_from_identifier(folder->account->trash_folder));
    584 	}
    585 }
    586 
    587 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
    588 {
    589 	PrefsAccount *mailac = NULL;
    590 	FILE *fp;
    591 	int hnum;
    592 	gchar *buf = NULL;
    593 	static HeaderEntry qentry[] = {{"S:",    NULL, FALSE},
    594 				       {"SSV:",  NULL, FALSE},
    595 				       {"R:",    NULL, FALSE},
    596 				       {"NG:",   NULL, FALSE},
    597 				       {"MAID:", NULL, FALSE},
    598 				       {"NAID:", NULL, FALSE},
    599 				       {"SCF:",  NULL, FALSE},
    600 				       {"RMID:", NULL, FALSE},
    601 				       {"FMID:", NULL, FALSE},
    602 				       {"X-Claws-End-Special-Headers",    NULL, FALSE},
    603 				       {NULL,    NULL, FALSE}};
    604 
    605 	cm_return_val_if_fail(file != NULL, NULL);
    606 
    607 	if ((fp = g_fopen(file, "rb")) == NULL) {
    608 		FILE_OP_ERROR(file, "g_fopen");
    609 		return NULL;
    610 	}
    611 
    612 	while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
    613 		gchar *p = buf + strlen(qentry[hnum].name);
    614 
    615 		if (hnum == Q_MAIL_ACCOUNT_ID) {
    616 			mailac = account_find_from_id(atoi(p));
    617 			break;
    618 		}
    619 		g_free(buf);
    620 		buf = NULL;
    621 	}
    622 	if (buf)
    623 		g_free(buf);
    624 	fclose(fp);
    625 	return mailac;
    626 }
    627 
    628 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
    629 {
    630 	gchar *folder_id;
    631 	const gchar *msgid;
    632 	gchar *id;
    633 
    634 	cm_return_val_if_fail(msginfo != NULL, NULL);
    635 	folder_id = folder_item_get_identifier(msginfo->folder);
    636 	msgid = msginfo->msgid;
    637 
    638 	id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
    639 
    640 	g_free(folder_id);
    641 
    642 	return id;
    643 }
    644 
    645 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
    646 {
    647 	gchar *folder_id = g_strdup(id);
    648 	gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
    649 	const gchar *msgid;
    650 	FolderItem *item;
    651 	MsgInfo *msginfo;
    652 
    653 	if (separator == NULL) {
    654 		g_free(folder_id);
    655 		return NULL;
    656 	}
    657 
    658 	*separator = '\0';
    659 	msgid = separator + 1;
    660 
    661 	item = folder_find_item_from_identifier(folder_id);
    662 
    663 	if (item == NULL) {
    664 		g_free(folder_id);
    665 		return NULL;
    666 	}
    667 
    668 	msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
    669 	g_free(folder_id);
    670 
    671 	return msginfo;
    672 }
    673 
    674 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
    675 {
    676 	GSList *result = NULL;
    677 	GSList *orig = NULL;
    678 	PrefsAccount *last_account = NULL;
    679 	MsgInfo *msg = NULL;
    680 	GSList *cur = NULL;
    681 	gboolean nothing_to_sort = TRUE;
    682 
    683 	if (!list)
    684 		return NULL;
    685 
    686 	orig = g_slist_copy(list);
    687 
    688 	msg = (MsgInfo *)orig->data;
    689 
    690 	for (cur = orig; cur; cur = cur->next)
    691 		debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
    692 
    693 	debug_print("\n");
    694 
    695 parse_again:
    696 	nothing_to_sort = TRUE;
    697 	cur = orig;
    698 	while (cur) {
    699 		gchar *file = NULL;
    700 		PrefsAccount *ac = NULL;
    701 		msg = (MsgInfo *)cur->data;
    702 		file = folder_item_fetch_msg(queue, msg->msgnum);
    703 		ac = procmsg_get_account_from_file(file);
    704 		g_free(file);
    705 
    706 		if (last_account == NULL || (ac != NULL && ac == last_account)) {
    707 			result = g_slist_append(result, msg);
    708 			orig = g_slist_remove(orig, msg);
    709 			last_account = ac;
    710 			nothing_to_sort = FALSE;
    711 			goto parse_again;
    712 		}
    713 		cur = cur->next;
    714 	}
    715 
    716 	if (orig && g_slist_length(orig)) {
    717 		if (!last_account && nothing_to_sort) {
    718 			/* can't find an account for the rest of the list */
    719 			cur = orig;
    720 			while (cur) {
    721 				result = g_slist_append(result, cur->data);
    722 				cur = cur->next;
    723 			}
    724 		} else {
    725 			last_account = NULL;
    726 			goto parse_again;
    727 		}
    728 	}
    729 
    730 	g_slist_free(orig);
    731 
    732 	for (cur = result; cur; cur = cur->next)
    733 		debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
    734 
    735 	debug_print("\n");
    736 
    737 	return result;
    738 }
    739 
    740 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
    741 {
    742 	gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
    743 	PrefsAccount *ac = procmsg_get_account_from_file(file);
    744 	GSList *cur;
    745 	g_free(file);
    746 	for (cur = elem; cur; cur = cur->next) {
    747 		MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
    748 		file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
    749 
    750 		if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
    751 			if (procmsg_get_account_from_file(file) == ac) {
    752 				g_free(file);
    753 				return FALSE;
    754 			}
    755 		}
    756 
    757 		g_free(file);
    758 	}
    759 	return TRUE;
    760 }
    761 
    762 static gboolean send_queue_lock = FALSE;
    763 
    764 gboolean procmsg_queue_lock(char **errstr)
    765 {
    766 	if (send_queue_lock) {
    767 		/* Avoid having to translate two similar strings */
    768 		log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
    769 		if (errstr) {
    770 			if (*errstr) g_free(*errstr);
    771 			*errstr = g_strdup_printf(_("Already trying to send."));
    772 		}
    773 		return FALSE;
    774 	}
    775 	send_queue_lock = TRUE;
    776 	return TRUE;
    777 }
    778 void procmsg_queue_unlock(void)
    779 {
    780 	send_queue_lock = FALSE;
    781 }
    782 /*!
    783  *\brief	Send messages in queue
    784  *
    785  *\param	queue Queue folder to process
    786  *\param	save_msgs Unused
    787  *
    788  *\return	Number of messages sent, negative if an error occurred
    789  *		positive if no error occurred
    790  */
    791 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
    792 {
    793 	gint sent = 0, err = 0;
    794 	GSList *list, *elem;
    795 	GSList *sorted_list = NULL;
    796 	GNode *node, *next;
    797 
    798 	if (!procmsg_queue_lock(errstr)) {
    799 		main_window_set_menu_sensitive(mainwindow_get_mainwindow());
    800 		toolbar_main_set_sensitive(mainwindow_get_mainwindow());
    801 		return -1;
    802 	}
    803 	inc_lock();
    804 	if (!queue)
    805 		queue = folder_get_default_queue();
    806 
    807 	if (queue == NULL) {
    808 		procmsg_queue_unlock();
    809 		inc_unlock();
    810 		return -1;
    811 	}
    812 
    813 	main_window_set_menu_sensitive(mainwindow_get_mainwindow());
    814 	toolbar_main_set_sensitive(mainwindow_get_mainwindow());
    815 
    816 	folder_item_scan(queue);
    817 	list = folder_item_get_msg_list(queue);
    818 
    819 	/* sort the list per sender account; this helps reusing the same SMTP server */
    820 	sorted_list = procmsg_list_sort_by_account(queue, list);
    821 
    822 	for (elem = sorted_list; elem != NULL; elem = elem->next) {
    823 		gchar *file;
    824 		MsgInfo *msginfo;
    825 
    826 		msginfo = (MsgInfo *)(elem->data);
    827 		if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
    828 			file = folder_item_fetch_msg(queue, msginfo->msgnum);
    829 			if (file) {
    830 				gboolean queued_removed = FALSE;
    831 				if (procmsg_send_message_queue_full(file,
    832 						!procmsg_is_last_for_account(queue, msginfo, elem),
    833 						errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
    834 					g_warning("sending queued message %d failed",
    835 						  msginfo->msgnum);
    836 					err++;
    837 				} else {
    838 					sent++;
    839 					if (!queued_removed)
    840 						folder_item_remove_msg(queue, msginfo->msgnum);
    841 				}
    842 				g_free(file);
    843 			}
    844 		}
    845 		/* FIXME: supposedly if only one message is locked, and queue
    846 		 * is being flushed, the following free says something like
    847 		 * "freeing msg ## in folder (nil)". */
    848 		procmsg_msginfo_free(&msginfo);
    849 	}
    850 
    851 	g_slist_free(sorted_list);
    852 	folder_item_scan(queue);
    853 
    854 	if (queue->node && queue->node->children) {
    855 		node = queue->node->children;
    856 		while (node != NULL) {
    857 			int res = 0;
    858 			next = node->next;
    859 			send_queue_lock = FALSE;
    860 			res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
    861 			send_queue_lock = TRUE;
    862 			if (res < 0)
    863 				err = -res;
    864 			else
    865 				sent += res;
    866 			node = next;
    867 		}
    868 	}
    869 	procmsg_queue_unlock();
    870 	inc_unlock();
    871 	main_window_set_menu_sensitive(mainwindow_get_mainwindow());
    872 	toolbar_main_set_sensitive(mainwindow_get_mainwindow());
    873 
    874 	return (err != 0 ? -err : sent);
    875 }
    876 
    877 gboolean procmsg_is_sending(void)
    878 {
    879 	return send_queue_lock;
    880 }
    881 
    882 /*!
    883  *\brief	Determine if a queue folder is empty
    884  *
    885  *\param	queue Queue folder to process
    886  *
    887  *\return	TRUE if the queue folder is empty, otherwise return FALSE
    888  */
    889 gboolean procmsg_queue_is_empty(FolderItem *queue)
    890 {
    891 	GSList *list;
    892 	gboolean res = FALSE;
    893 	if (!queue)
    894 		queue = folder_get_default_queue();
    895 	cm_return_val_if_fail(queue != NULL, TRUE);
    896 
    897 	folder_item_scan(queue);
    898 	list = folder_item_get_msg_list(queue);
    899 	res = (list == NULL);
    900 	procmsg_msg_list_free(list);
    901 
    902 	if (res == TRUE) {
    903 		GNode *node, *next;
    904 		if (queue->node && queue->node->children) {
    905 			node = queue->node->children;
    906 			while (node != NULL) {
    907 				next = node->next;
    908 				if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
    909 					return FALSE;
    910 				node = next;
    911 			}
    912 		}
    913 	}
    914 	return res;
    915 }
    916 
    917 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file)
    918 {
    919 	gint num;
    920 	MsgInfo *msginfo, *tmp_msginfo;
    921 	MsgFlags flag = {0, 0};
    922 	gchar *outbox_path = NULL;
    923 
    924 	if (!outbox) {
    925 		debug_print("using default outbox\n");
    926 		outbox = folder_get_default_outbox();
    927 	}
    928 
    929 	cm_return_val_if_fail(outbox != NULL, -1);
    930 
    931 	outbox_path = folder_item_get_path(outbox);
    932 	debug_print("attempting to save sent message to %s...\n", outbox_path);
    933 	g_free(outbox_path);
    934 
    935 	gchar tmp[MAXPATHLEN + 1];
    936 
    937 	g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
    938 		   get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
    939 
    940 	char *end = "X-Claws-End-Special-Headers:";
    941 	if (fence_copy_after_line(tmp, file, end) == 0)
    942 		return -1;
    943 
    944 	while (folder_item_scan(outbox) < 0) {
    945 		outbox = foldersel_folder_sel(NULL, FOLDER_SEL_SAVE, NULL, FALSE,
    946 				_("Select the folder where you want to save the sent message"));
    947 		if (outbox == NULL) {
    948 			g_warning("not saving sent message");
    949 			unlink(tmp);
    950 			return -1;
    951 		}
    952 	}
    953 	if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
    954 		g_warning("not saving sent message");
    955 		outbox_path = folder_item_get_path(outbox);
    956 		alertpanel_warning(_("Could not save sent message to %s."), outbox_path);
    957 		unlink(tmp);
    958 		g_free(outbox_path);
    959 		return -1;
    960 	}
    961 
    962 	msginfo = folder_item_get_msginfo(outbox, num);		/* refcnt++ */
    963 	tmp_msginfo = procmsg_msginfo_get_full_info(msginfo);	/* refcnt++ */
    964 	if (msginfo != NULL) {
    965 		procmsg_msginfo_unset_flags(msginfo, ~0, 0);
    966 		procmsg_msginfo_free(&msginfo);			/* refcnt-- */
    967 		/* tmp_msginfo == msginfo */
    968 		if (tmp_msginfo && msginfo->extradata &&
    969 		    (msginfo->extradata->dispositionnotificationto ||
    970 		     msginfo->extradata->returnreceiptto)) {
    971 			procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
    972 		}
    973 		procmsg_msginfo_free(&tmp_msginfo);		/* refcnt-- */
    974 	}
    975 
    976 	return 0;
    977 }
    978 
    979 
    980 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
    981 {
    982 	msginfo->refcnt++;
    983 
    984 	return msginfo;
    985 }
    986 
    987 MsgInfo *procmsg_msginfo_new(void)
    988 {
    989 	MsgInfo *newmsginfo;
    990 
    991 	newmsginfo = g_new0(MsgInfo, 1);
    992 	newmsginfo->refcnt = 1;
    993 
    994 	return newmsginfo;
    995 }
    996 
    997 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
    998 {
    999 	MsgInfo *newmsginfo;
   1000         GSList *refs;
   1001 
   1002 	if (msginfo == NULL) return NULL;
   1003 
   1004 	newmsginfo = g_new0(MsgInfo, 1);
   1005 
   1006 	newmsginfo->refcnt = 1;
   1007 
   1008 #define MEMBCOPY(mmb)	newmsginfo->mmb = msginfo->mmb
   1009 #define MEMBDUP(mmb)	newmsginfo->mmb = msginfo->mmb ? \
   1010 			g_strdup(msginfo->mmb) : NULL
   1011 
   1012 	MEMBCOPY(msgnum);
   1013 	MEMBCOPY(size);
   1014 	MEMBCOPY(mtime);
   1015 	MEMBCOPY(date_t);
   1016 
   1017 	MEMBCOPY(flags);
   1018 
   1019 	MEMBDUP(fromname);
   1020 
   1021 	MEMBDUP(date);
   1022 	MEMBDUP(from);
   1023 	MEMBDUP(to);
   1024 	MEMBDUP(cc);
   1025 	MEMBDUP(subject);
   1026 	MEMBDUP(msgid);
   1027 	MEMBDUP(inreplyto);
   1028 	MEMBDUP(xref);
   1029 
   1030 	MEMBCOPY(folder);
   1031 	MEMBCOPY(to_folder);
   1032 
   1033 	if (msginfo->extradata) {
   1034 		newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
   1035 		MEMBDUP(extradata->dispositionnotificationto);
   1036 		MEMBDUP(extradata->returnreceiptto);
   1037 		MEMBDUP(extradata->partial_recv);
   1038 		MEMBDUP(extradata->account_server);
   1039 		MEMBDUP(extradata->account_login);
   1040 		MEMBDUP(extradata->list_post);
   1041 		MEMBDUP(extradata->list_subscribe);
   1042 		MEMBDUP(extradata->list_unsubscribe);
   1043 		MEMBDUP(extradata->list_help);
   1044 		MEMBDUP(extradata->list_archive);
   1045 		MEMBDUP(extradata->list_owner);
   1046 		MEMBDUP(extradata->resent_from);
   1047 	}
   1048 
   1049         refs = msginfo->references;
   1050         for (refs = msginfo->references; refs != NULL; refs = refs->next) {
   1051                 newmsginfo->references = g_slist_prepend
   1052                         (newmsginfo->references, g_strdup(refs->data));
   1053         }
   1054         newmsginfo->references = g_slist_reverse(newmsginfo->references);
   1055 
   1056 	MEMBCOPY(score);
   1057 	MEMBDUP(plaintext_file);
   1058 
   1059 	return newmsginfo;
   1060 }
   1061 
   1062 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
   1063 {
   1064 	MsgInfo *full_msginfo;
   1065 
   1066 	if (msginfo == NULL) return NULL;
   1067 
   1068 	if (!file || !is_file_exist(file)) {
   1069 		g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file");
   1070 		return NULL;
   1071 	}
   1072 
   1073 	full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
   1074 	if (!full_msginfo) return NULL;
   1075 
   1076 	msginfo->total_size = full_msginfo->total_size;
   1077 	msginfo->planned_download = full_msginfo->planned_download;
   1078 
   1079 	if (full_msginfo->extradata) {
   1080 		if (!msginfo->extradata)
   1081 			msginfo->extradata = g_new0(MsgInfoExtraData, 1);
   1082 		if (!msginfo->extradata->list_post)
   1083 			msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
   1084 		if (!msginfo->extradata->list_subscribe)
   1085 			msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
   1086 		if (!msginfo->extradata->list_unsubscribe)
   1087 			msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
   1088 		if (!msginfo->extradata->list_help)
   1089 			msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
   1090 		if (!msginfo->extradata->list_archive)
   1091 			msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
   1092 		if (!msginfo->extradata->list_owner)
   1093 			msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
   1094 		if (!msginfo->extradata->dispositionnotificationto)
   1095 			msginfo->extradata->dispositionnotificationto =
   1096 				g_strdup(full_msginfo->extradata->dispositionnotificationto);
   1097 		if (!msginfo->extradata->returnreceiptto)
   1098 			msginfo->extradata->returnreceiptto = g_strdup
   1099 				(full_msginfo->extradata->returnreceiptto);
   1100 		if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
   1101 			msginfo->extradata->partial_recv = g_strdup(full_msginfo->extradata->partial_recv);
   1102 		if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
   1103 			msginfo->extradata->account_server = g_strdup
   1104 				(full_msginfo->extradata->account_server);
   1105 		if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
   1106 			msginfo->extradata->account_login = g_strdup
   1107 				(full_msginfo->extradata->account_login);
   1108 		if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
   1109 			msginfo->extradata->resent_from = g_strdup
   1110 				(full_msginfo->extradata->resent_from);
   1111 	}
   1112 	procmsg_msginfo_free(&full_msginfo);
   1113 
   1114 	return procmsg_msginfo_new_ref(msginfo);
   1115 }
   1116 
   1117 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
   1118 {
   1119 	MsgInfo *full_msginfo;
   1120 	gchar *file;
   1121 
   1122 	if (msginfo == NULL) return NULL;
   1123 
   1124 	file = procmsg_get_message_file_path(msginfo);
   1125 	if (!file || !is_file_exist(file)) {
   1126 		g_free(file);
   1127 		file = procmsg_get_message_file(msginfo);
   1128 	}
   1129 	if (!file || !is_file_exist(file)) {
   1130 		g_warning("procmsg_msginfo_get_full_info(): can't get message file");
   1131 		return NULL;
   1132 	}
   1133 
   1134 	full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
   1135 	g_free(file);
   1136 	return full_msginfo;
   1137 }
   1138 
   1139 #define FREENULL(n) { g_free(n); n = NULL; }
   1140 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
   1141 {
   1142 	MsgInfo *msginfo = *msginfo_ptr;
   1143 
   1144 	if (msginfo == NULL) return;
   1145 
   1146 	msginfo->refcnt--;
   1147 	if (msginfo->refcnt > 0)
   1148 		return;
   1149 
   1150 	if (msginfo->to_folder) {
   1151 		msginfo->to_folder->op_count--;
   1152 		folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
   1153 	}
   1154 
   1155 	FREENULL(msginfo->fromspace);
   1156 
   1157 	FREENULL(msginfo->fromname);
   1158 
   1159 	FREENULL(msginfo->date);
   1160 	FREENULL(msginfo->from);
   1161 	FREENULL(msginfo->to);
   1162 	FREENULL(msginfo->cc);
   1163 	FREENULL(msginfo->subject);
   1164 	FREENULL(msginfo->msgid);
   1165 	FREENULL(msginfo->inreplyto);
   1166 	FREENULL(msginfo->xref);
   1167 
   1168 	if (msginfo->extradata) {
   1169 		FREENULL(msginfo->extradata->returnreceiptto);
   1170 		FREENULL(msginfo->extradata->dispositionnotificationto);
   1171 		FREENULL(msginfo->extradata->list_post);
   1172 		FREENULL(msginfo->extradata->list_subscribe);
   1173 		FREENULL(msginfo->extradata->list_unsubscribe);
   1174 		FREENULL(msginfo->extradata->list_help);
   1175 		FREENULL(msginfo->extradata->list_archive);
   1176 		FREENULL(msginfo->extradata->list_owner);
   1177 		FREENULL(msginfo->extradata->partial_recv);
   1178 		FREENULL(msginfo->extradata->account_server);
   1179 		FREENULL(msginfo->extradata->account_login);
   1180 		FREENULL(msginfo->extradata->resent_from);
   1181 		FREENULL(msginfo->extradata);
   1182 	}
   1183 	slist_free_strings_full(msginfo->references);
   1184 	msginfo->references = NULL;
   1185 	g_slist_free(msginfo->tags);
   1186 	msginfo->tags = NULL;
   1187 
   1188 	FREENULL(msginfo->plaintext_file);
   1189 
   1190 	g_free(msginfo);
   1191 	*msginfo_ptr = NULL;
   1192 }
   1193 #undef FREENULL
   1194 
   1195 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
   1196 {
   1197 	guint memusage = 0;
   1198 	GSList *tmp;
   1199 
   1200 	memusage += sizeof(MsgInfo);
   1201 	if (msginfo->fromname)
   1202 		memusage += strlen(msginfo->fromname);
   1203 	if (msginfo->date)
   1204 		memusage += strlen(msginfo->date);
   1205 	if (msginfo->from)
   1206 		memusage += strlen(msginfo->from);
   1207 	if (msginfo->to)
   1208 		memusage += strlen(msginfo->to);
   1209 	if (msginfo->cc)
   1210 		memusage += strlen(msginfo->cc);
   1211 	if (msginfo->subject)
   1212 		memusage += strlen(msginfo->subject);
   1213 	if (msginfo->msgid)
   1214 		memusage += strlen(msginfo->msgid);
   1215 	if (msginfo->inreplyto)
   1216 		memusage += strlen(msginfo->inreplyto);
   1217 
   1218 	for (tmp = msginfo->references; tmp; tmp=tmp->next) {
   1219 		gchar *r = (gchar *)tmp->data;
   1220 		memusage += r?strlen(r):0 + sizeof(GSList);
   1221 	}
   1222 	if (msginfo->fromspace)
   1223 		memusage += strlen(msginfo->fromspace);
   1224 
   1225 	for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
   1226 		memusage += sizeof(GSList);
   1227 	}
   1228 	if (msginfo->extradata) {
   1229 		memusage += sizeof(MsgInfoExtraData);
   1230 		if (msginfo->extradata->dispositionnotificationto)
   1231 			memusage += strlen(msginfo->extradata->dispositionnotificationto);
   1232 		if (msginfo->extradata->returnreceiptto)
   1233 			memusage += strlen(msginfo->extradata->returnreceiptto);
   1234 
   1235 		if (msginfo->extradata->partial_recv)
   1236 			memusage += strlen(msginfo->extradata->partial_recv);
   1237 		if (msginfo->extradata->account_server)
   1238 			memusage += strlen(msginfo->extradata->account_server);
   1239 		if (msginfo->extradata->account_login)
   1240 			memusage += strlen(msginfo->extradata->account_login);
   1241 		if (msginfo->extradata->resent_from)
   1242 			memusage += strlen(msginfo->extradata->resent_from);
   1243 
   1244 		if (msginfo->extradata->list_post)
   1245 			memusage += strlen(msginfo->extradata->list_post);
   1246 		if (msginfo->extradata->list_subscribe)
   1247 			memusage += strlen(msginfo->extradata->list_subscribe);
   1248 		if (msginfo->extradata->list_unsubscribe)
   1249 			memusage += strlen(msginfo->extradata->list_unsubscribe);
   1250 		if (msginfo->extradata->list_help)
   1251 			memusage += strlen(msginfo->extradata->list_help);
   1252 		if (msginfo->extradata->list_archive)
   1253 			memusage += strlen(msginfo->extradata->list_archive);
   1254 		if (msginfo->extradata->list_owner)
   1255 			memusage += strlen(msginfo->extradata->list_owner);
   1256 	}
   1257 	return memusage;
   1258 }
   1259 
   1260 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
   1261 					    FolderItem *queue, gint msgnum, gboolean *queued_removed)
   1262 {
   1263 	static HeaderEntry qentry[] = {
   1264 				       {"S:",    NULL, FALSE}, /* 0 */
   1265 				       {"SSV:",  NULL, FALSE},
   1266 				       {"R:",    NULL, FALSE},
   1267 				       {"NG:",   NULL, FALSE},
   1268 				       {"MAID:", NULL, FALSE},
   1269 				       {"NAID:", NULL, FALSE}, /* 5 */
   1270 				       {"SCF:",  NULL, FALSE},
   1271 				       {"RMID:", NULL, FALSE},
   1272 				       {"FMID:", NULL, FALSE},
   1273 				       {"X-Claws-End-Special-Headers:", NULL, FALSE},
   1274 				       {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
   1275 				       {NULL,    NULL, FALSE}};
   1276 	FILE *fp;
   1277 	gint filepos;
   1278 	gint mailval = 0;
   1279 	gchar *from = NULL;
   1280 	gchar *smtpserver = NULL;
   1281 	GSList *to_list = NULL;
   1282 	gchar *savecopyfolder = NULL;
   1283 	gchar *replymessageid = NULL;
   1284 	gchar *fwdmessageid = NULL;
   1285 	gchar *buf;
   1286 	gint hnum;
   1287 	PrefsAccount *mailac = NULL;
   1288 	FolderItem *outbox;
   1289 
   1290 	cm_return_val_if_fail(file != NULL, -1);
   1291 
   1292 	if ((fp = g_fopen(file, "rb")) == NULL) {
   1293 		FILE_OP_ERROR(file, "g_fopen");
   1294 		if (errstr) {
   1295 			if (*errstr) g_free(*errstr);
   1296 			*errstr = g_strdup_printf(_("Couldn't open file %s."), file);
   1297 		}
   1298 		return -1;
   1299 	}
   1300 
   1301 	while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
   1302 		gchar *p = buf + strlen(qentry[hnum].name);
   1303 
   1304 		switch (hnum) {
   1305 		case Q_SENDER:
   1306 			if (from == NULL)
   1307 				from = g_strdup(p);
   1308 			break;
   1309 		case Q_SMTPSERVER:
   1310 			if (smtpserver == NULL)
   1311 				smtpserver = g_strdup(p);
   1312 			break;
   1313 		case Q_RECIPIENTS:
   1314 			to_list = address_list_append(to_list, p);
   1315 			break;
   1316 		case Q_MAIL_ACCOUNT_ID:
   1317 			mailac = account_find_from_id(atoi(p));
   1318 			break;
   1319 		case Q_SAVE_COPY_FOLDER:
   1320 			if (savecopyfolder == NULL)
   1321 				savecopyfolder = g_strdup(p);
   1322 			break;
   1323 		case Q_REPLY_MESSAGE_ID:
   1324 			if (replymessageid == NULL)
   1325 				replymessageid = g_strdup(p);
   1326 			break;
   1327 		case Q_FWD_MESSAGE_ID:
   1328 			if (fwdmessageid == NULL)
   1329 				fwdmessageid = g_strdup(p);
   1330 			break;
   1331 		case Q_CLAWS_HDRS:
   1332 		case Q_CLAWS_HDRS_OLD:
   1333 			/* end of special headers reached */
   1334 			g_free(buf);
   1335 			goto send_mail; /* can't "break;break;" */
   1336 		}
   1337 		g_free(buf);
   1338 	}
   1339 
   1340 send_mail:
   1341 	filepos = ftell(fp);
   1342 	if (filepos < 0) {
   1343 		FILE_OP_ERROR(file, "ftell");
   1344 		if (errstr) {
   1345 			if (*errstr) g_free(*errstr);
   1346 			*errstr = g_strdup_printf(_("Couldn't open file %s."), file);
   1347 		}
   1348 		return -1;
   1349 	}
   1350 
   1351 	if (to_list) {
   1352 		debug_print("Sending message by mail\n");
   1353 		if (!from) {
   1354 			if (errstr) {
   1355 				if (*errstr) g_free(*errstr);
   1356 				*errstr = g_strdup_printf(_("Queued message header is broken."));
   1357 			}
   1358 			mailval = -1;
   1359 		} else if (mailac && mailac->use_mail_command &&
   1360 			   mailac->mail_command && (* mailac->mail_command)) {
   1361 			mailval = send_message_local(mailac->mail_command, fp);
   1362 		} else {
   1363 			if (!mailac) {
   1364 				mailac = account_find_from_smtp_server(from, smtpserver);
   1365 				if (!mailac) {
   1366 					g_warning("account not found, "
   1367 						    "using current account...");
   1368 					mailac = cur_account;
   1369 				}
   1370 			}
   1371 
   1372 			if (mailac) {
   1373 				mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
   1374 				if (mailval == -1 && errstr) {
   1375 					if (*errstr) g_free(*errstr);
   1376 					*errstr = g_strdup_printf(_("An error happened during SMTP session."));
   1377 				}
   1378 			} else {
   1379 				PrefsAccount tmp_ac;
   1380 
   1381 				g_warning("account not found");
   1382 
   1383 				memset(&tmp_ac, 0, sizeof(PrefsAccount));
   1384 				tmp_ac.address = from;
   1385 				tmp_ac.smtp_server = smtpserver;
   1386 				tmp_ac.smtpport = SMTP_PORT;
   1387 				mailval = send_message_smtp(&tmp_ac, to_list, fp);
   1388 				if (mailval == -1 && errstr) {
   1389 					if (*errstr) g_free(*errstr);
   1390 					*errstr = g_strdup_printf(_("No specific account has been found to "
   1391 							"send, and an error happened during SMTP session."));
   1392 				}
   1393 			}
   1394 		}
   1395 	} else if (!to_list) {
   1396 		if (errstr) {
   1397 			if (*errstr) g_free(*errstr);
   1398 			*errstr = g_strdup(_("Couldn't determine sending information. "
   1399 				"Maybe the email hasn't been generated by Claws Mail."));
   1400 		}
   1401 		mailval = -1;
   1402 	}
   1403 
   1404 	if (fseek(fp, filepos, SEEK_SET) < 0) {
   1405 		FILE_OP_ERROR(file, "fseek");
   1406 		mailval = -1;
   1407 	}
   1408 
   1409 	fclose(fp);
   1410 
   1411 	/* save message to outbox */
   1412 	if (mailval == 0 && savecopyfolder) {
   1413 		debug_print("saving sent message to %s...\n", savecopyfolder);
   1414 		outbox = folder_find_item_from_identifier(savecopyfolder);
   1415 		if (!outbox) {
   1416 			gchar *id;
   1417 			outbox = folder_get_default_outbox();
   1418 			if (outbox != NULL) {
   1419 				id = folder_item_get_identifier(outbox);
   1420 				debug_print("%s not found, using %s\n", savecopyfolder, id);
   1421 				g_free(id);
   1422 			} else {
   1423 				debug_print("could not find outbox\n");
   1424 			}
   1425 		}
   1426 		gboolean saved = FALSE;
   1427 		*queued_removed = FALSE;
   1428 		if (queue && msgnum > 0) {
   1429 			MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
   1430 			if (folder_item_move_msg(outbox, queued_mail) >= 0) {
   1431 				debug_print("moved queued mail %d to sent folder\n", msgnum);
   1432 				saved = TRUE;
   1433 				*queued_removed = TRUE;
   1434 			} else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
   1435 				debug_print("copied queued mail %d to sent folder\n", msgnum);
   1436 				saved = TRUE;
   1437 			}
   1438 			procmsg_msginfo_free(&queued_mail);
   1439 		}
   1440 		if (!saved) {
   1441 			debug_print("resaving queued mail to sent folder\n");
   1442 			procmsg_save_to_outbox(outbox, file);
   1443 		}
   1444 	}
   1445 
   1446 	if (replymessageid != NULL || fwdmessageid != NULL) {
   1447 		gchar **tokens;
   1448 		FolderItem *item;
   1449 
   1450 		if (replymessageid != NULL)
   1451 			tokens = g_strsplit(replymessageid, "\t", 0);
   1452 		else
   1453 			tokens = g_strsplit(fwdmessageid, "\t", 0);
   1454 		item = folder_find_item_from_identifier(tokens[0]);
   1455 
   1456 		/* check if queued message has valid folder and message id */
   1457 		if (item != NULL && tokens[2] != NULL) {
   1458 			MsgInfo *msginfo;
   1459 
   1460 			msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
   1461 
   1462 			/* check if referring message exists and has a message id */
   1463 			if ((msginfo != NULL) &&
   1464 			    (msginfo->msgid != NULL) &&
   1465 			    (strcmp(msginfo->msgid, tokens[2]) != 0)) {
   1466 				procmsg_msginfo_free(&msginfo);
   1467 				msginfo = NULL;
   1468 			}
   1469 
   1470 			if (msginfo == NULL) {
   1471 				msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
   1472 			}
   1473 
   1474 			if (msginfo != NULL) {
   1475 				if (replymessageid != NULL) {
   1476 					MsgPermFlags to_unset = 0;
   1477 
   1478 					if (prefs_common.mark_as_read_on_new_window)
   1479 						to_unset = (MSG_NEW|MSG_UNREAD);
   1480 
   1481 					procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
   1482 					procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
   1483 				}  else {
   1484 					procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
   1485 				}
   1486 				procmsg_msginfo_free(&msginfo);
   1487 			}
   1488 		}
   1489 		g_strfreev(tokens);
   1490 	}
   1491 
   1492 	g_free(from);
   1493 	g_free(smtpserver);
   1494 	slist_free_strings_full(to_list);
   1495 	g_free(savecopyfolder);
   1496 	g_free(replymessageid);
   1497 	g_free(fwdmessageid);
   1498 
   1499 	return (mailval);
   1500 }
   1501 
   1502 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
   1503 {
   1504 	gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
   1505 	main_window_set_menu_sensitive(mainwindow_get_mainwindow());
   1506 	toolbar_main_set_sensitive(mainwindow_get_mainwindow());
   1507 	return result;
   1508 }
   1509 
   1510 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
   1511 {
   1512 	gint val;
   1513 	if (procmsg_queue_lock(errstr)) {
   1514 		val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
   1515 		procmsg_queue_unlock();
   1516 		return val;
   1517 	}
   1518 	return -1;
   1519 }
   1520 
   1521 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
   1522 {
   1523 	MsgPermFlags new_flags = msginfo->flags.perm_flags;
   1524 
   1525 	/* NEW flag */
   1526 	if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
   1527 		item->new_msgs++;
   1528 	}
   1529 
   1530 	if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
   1531 		item->new_msgs--;
   1532 	}
   1533 
   1534 	/* UNREAD flag */
   1535 	if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
   1536 		item->unread_msgs++;
   1537 		if (procmsg_msg_has_marked_parent(msginfo))
   1538 			item->unreadmarked_msgs++;
   1539 	}
   1540 
   1541 	if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
   1542 		item->unread_msgs--;
   1543 		if (procmsg_msg_has_marked_parent(msginfo))
   1544 			item->unreadmarked_msgs--;
   1545 	}
   1546 
   1547 	/* MARK flag */
   1548 	if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
   1549 		procmsg_update_unread_children(msginfo, TRUE);
   1550 		item->marked_msgs++;
   1551 	}
   1552 
   1553 	if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
   1554 		procmsg_update_unread_children(msginfo, FALSE);
   1555 		item->marked_msgs--;
   1556 	}
   1557 
   1558 	if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
   1559 		item->replied_msgs++;
   1560 	}
   1561 
   1562 	if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
   1563 		item->replied_msgs--;
   1564 	}
   1565 
   1566 	if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
   1567 		item->forwarded_msgs++;
   1568 	}
   1569 
   1570 	if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
   1571 		item->forwarded_msgs--;
   1572 	}
   1573 
   1574 	if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
   1575 		item->locked_msgs++;
   1576 	}
   1577 
   1578 	if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
   1579 		item->locked_msgs--;
   1580 	}
   1581 
   1582 	if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
   1583 		item->ignored_msgs--;
   1584 	}
   1585 
   1586 	if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
   1587 		item->ignored_msgs++;
   1588 	}
   1589 
   1590 	if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
   1591 		item->watched_msgs--;
   1592 	}
   1593 
   1594 	if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
   1595 		item->watched_msgs++;
   1596 	}
   1597 }
   1598 
   1599 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
   1600 {
   1601 	FolderItem *item;
   1602 	MsgInfoUpdate msginfo_update;
   1603 	MsgPermFlags perm_flags_new, perm_flags_old;
   1604 	MsgTmpFlags tmp_flags_old;
   1605 
   1606 	cm_return_if_fail(msginfo != NULL);
   1607 	item = msginfo->folder;
   1608 	cm_return_if_fail(item != NULL);
   1609 
   1610 	debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
   1611 
   1612 	/* Perm Flags handling */
   1613 	perm_flags_old = msginfo->flags.perm_flags;
   1614 	perm_flags_new = msginfo->flags.perm_flags | perm_flags;
   1615 	if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
   1616 		perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
   1617 	}
   1618 	if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
   1619 		perm_flags_new &= ~(MSG_IGNORE_THREAD);
   1620 	}
   1621 
   1622 	if (perm_flags_old != perm_flags_new) {
   1623 		folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
   1624 
   1625 		update_folder_msg_counts(item, msginfo, perm_flags_old);
   1626 		summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
   1627 	}
   1628 
   1629 	/* Tmp flags handling */
   1630 	tmp_flags_old = msginfo->flags.tmp_flags;
   1631 	msginfo->flags.tmp_flags |= tmp_flags;
   1632 
   1633 	/* update notification */
   1634 	if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
   1635 		msginfo_update.msginfo = msginfo;
   1636 		msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
   1637 		hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
   1638 		folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
   1639 	}
   1640 }
   1641 
   1642 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
   1643 {
   1644 	FolderItem *item;
   1645 	MsgInfoUpdate msginfo_update;
   1646 	MsgPermFlags perm_flags_new, perm_flags_old;
   1647 	MsgTmpFlags tmp_flags_old;
   1648 
   1649 	cm_return_if_fail(msginfo != NULL);
   1650 	item = msginfo->folder;
   1651 	cm_return_if_fail(item != NULL);
   1652 
   1653 	debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
   1654 
   1655 	/* Perm Flags handling */
   1656 	perm_flags_old = msginfo->flags.perm_flags;
   1657 	perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
   1658 
   1659 	if (perm_flags_old != perm_flags_new) {
   1660 		folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
   1661 
   1662 		update_folder_msg_counts(item, msginfo, perm_flags_old);
   1663 	}
   1664 
   1665 	/* Tmp flags hanlding */
   1666 	tmp_flags_old = msginfo->flags.tmp_flags;
   1667 	msginfo->flags.tmp_flags &= ~tmp_flags;
   1668 
   1669 	/* update notification */
   1670 	if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
   1671 		msginfo_update.msginfo = msginfo;
   1672 		msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
   1673 		hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
   1674 		folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
   1675 	}
   1676 }
   1677 
   1678 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
   1679 				MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
   1680 				MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
   1681 {
   1682 	FolderItem *item;
   1683 	MsgInfoUpdate msginfo_update;
   1684 	MsgPermFlags perm_flags_new, perm_flags_old;
   1685 	MsgTmpFlags tmp_flags_old;
   1686 
   1687 	cm_return_if_fail(msginfo != NULL);
   1688 	item = msginfo->folder;
   1689 	cm_return_if_fail(item != NULL);
   1690 
   1691 	debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
   1692 
   1693 	/* Perm Flags handling */
   1694 	perm_flags_old = msginfo->flags.perm_flags;
   1695 	perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
   1696 	if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
   1697 		perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
   1698 	}
   1699 	if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
   1700 		perm_flags_new &= ~(MSG_IGNORE_THREAD);
   1701 	}
   1702 
   1703 	if (perm_flags_old != perm_flags_new) {
   1704 		folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
   1705 
   1706 		update_folder_msg_counts(item, msginfo, perm_flags_old);
   1707 
   1708 	}
   1709 
   1710 	/* Tmp flags handling */
   1711 	tmp_flags_old = msginfo->flags.tmp_flags;
   1712 	msginfo->flags.tmp_flags &= ~rem_tmp_flags;
   1713 	msginfo->flags.tmp_flags |= add_tmp_flags;
   1714 
   1715 	/* update notification */
   1716 	if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
   1717 		msginfo_update.msginfo = msginfo;
   1718 		msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
   1719 		hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
   1720 		folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
   1721 	}
   1722 }
   1723 
   1724 /*!
   1725  *\brief	check for flags (e.g. mark) in prior msgs of current thread
   1726  *
   1727  *\param	info Current message
   1728  *\param	perm_flags Flags to be checked
   1729  *\param	parentmsgs Hash of prior msgs to avoid loops
   1730  *
   1731  *\return	gboolean TRUE if perm_flags are found
   1732  */
   1733 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
   1734 		MsgPermFlags perm_flags, GHashTable *parentmsgs)
   1735 {
   1736 	MsgInfo *tmp;
   1737 
   1738 	cm_return_val_if_fail(info != NULL, FALSE);
   1739 
   1740 	if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
   1741 		tmp = folder_item_get_msginfo_by_msgid(info->folder,
   1742 				info->inreplyto);
   1743 		if (tmp && (tmp->flags.perm_flags & perm_flags)) {
   1744 			procmsg_msginfo_free(&tmp);
   1745 			return TRUE;
   1746 		} else if (tmp != NULL) {
   1747 			gboolean result;
   1748 
   1749 			if (g_hash_table_lookup(parentmsgs, info)) {
   1750 				debug_print("loop detected: %d\n",
   1751 					info->msgnum);
   1752 				result = FALSE;
   1753 			} else {
   1754 				g_hash_table_insert(parentmsgs, info, "1");
   1755 				result = procmsg_msg_has_flagged_parent_real(
   1756 				    tmp, perm_flags, parentmsgs);
   1757 			}
   1758 			procmsg_msginfo_free(&tmp);
   1759 			return result;
   1760 		} else {
   1761 			return FALSE;
   1762 		}
   1763 	} else
   1764 		return FALSE;
   1765 }
   1766 
   1767 /*!
   1768  *\brief	Callback for cleaning up hash of parentmsgs
   1769  */
   1770 static gboolean parentmsgs_hash_remove(gpointer key,
   1771                             gpointer value,
   1772                             gpointer user_data)
   1773 {
   1774 	return TRUE;
   1775 }
   1776 
   1777 /*!
   1778  *\brief	Set up list of parentmsgs
   1779  *		See procmsg_msg_has_flagged_parent_real()
   1780  */
   1781 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
   1782 {
   1783 	gboolean result;
   1784 	static GHashTable *parentmsgs = NULL;
   1785 
   1786 	if (parentmsgs == NULL)
   1787 		parentmsgs = g_hash_table_new(NULL, NULL);
   1788 
   1789 	result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
   1790 	g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
   1791 
   1792 	return result;
   1793 }
   1794 
   1795 /*!
   1796  *\brief	Check if msgs prior in thread are marked
   1797  *		See procmsg_msg_has_flagged_parent_real()
   1798  */
   1799 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
   1800 {
   1801 	return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
   1802 }
   1803 
   1804 
   1805 static GSList *procmsg_find_children_func(MsgInfo *info,
   1806 				   GSList *children, GSList *all)
   1807 {
   1808 	GSList *cur;
   1809 
   1810 	cm_return_val_if_fail(info!=NULL, children);
   1811 	if (info->msgid == NULL)
   1812 		return children;
   1813 
   1814 	for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
   1815 		MsgInfo *tmp = (MsgInfo *)cur->data;
   1816 		if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
   1817 			/* Check if message is already in the list */
   1818 			if ((children == NULL) ||
   1819 			    (g_slist_index(children, tmp) == -1)) {
   1820 				children = g_slist_prepend(children,
   1821 						procmsg_msginfo_new_ref(tmp));
   1822 				children = procmsg_find_children_func(tmp,
   1823 							children,
   1824 							all);
   1825 			}
   1826 		}
   1827 	}
   1828 	return children;
   1829 }
   1830 
   1831 static GSList *procmsg_find_children (MsgInfo *info)
   1832 {
   1833 	GSList *children;
   1834 	GSList *all, *cur;
   1835 
   1836 	cm_return_val_if_fail(info!=NULL, NULL);
   1837 	all = folder_item_get_msg_list(info->folder);
   1838 	children = procmsg_find_children_func(info, NULL, all);
   1839 	if (children != NULL) {
   1840 		for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
   1841 			/* this will not free the used pointers
   1842 			   created with procmsg_msginfo_new_ref */
   1843 			procmsg_msginfo_free((MsgInfo **)&(cur->data));
   1844 		}
   1845 	}
   1846 	g_slist_free(all);
   1847 
   1848 	return children;
   1849 }
   1850 
   1851 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
   1852 {
   1853 	GSList *children = procmsg_find_children(info);
   1854 	GSList *cur;
   1855 	for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
   1856 		MsgInfo *tmp = (MsgInfo *)cur->data;
   1857 		if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
   1858 			if(newly_marked)
   1859 				info->folder->unreadmarked_msgs++;
   1860 			else
   1861 				info->folder->unreadmarked_msgs--;
   1862 			folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
   1863 		}
   1864 		procmsg_msginfo_free(&tmp);
   1865 	}
   1866 	g_slist_free(children);
   1867 }
   1868 
   1869 /**
   1870  * Set the destination folder for a copy or move operation
   1871  *
   1872  * \param msginfo The message which's destination folder is changed
   1873  * \param to_folder The destination folder for the operation
   1874  */
   1875 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
   1876 {
   1877 	if(msginfo->to_folder != NULL) {
   1878 		msginfo->to_folder->op_count--;
   1879 		folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
   1880 	}
   1881 	msginfo->to_folder = to_folder;
   1882 	if(to_folder != NULL) {
   1883 		to_folder->op_count++;
   1884 		folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
   1885 	}
   1886 }
   1887 
   1888 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
   1889 {
   1890 	MsgInfo *tmp_msginfo = NULL;
   1891 	MsgFlags flags = {0, 0};
   1892 	gchar *tmpfile = get_tmp_file();
   1893 	FILE *fp = g_fopen(tmpfile, "wb");
   1894 
   1895 	if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
   1896 	    g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
   1897 		g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
   1898 		if (fp)
   1899 			fclose(fp);
   1900 		g_free(tmpfile);
   1901 		return NULL;
   1902 	}
   1903 
   1904 	if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
   1905 		fclose(fp);
   1906 		fp = NULL;
   1907 		tmp_msginfo = procheader_parse_file(
   1908 			tmpfile, flags,
   1909 			TRUE, FALSE);
   1910 	}
   1911 	if (fp)
   1912 		fclose(fp);
   1913 
   1914 	if (tmp_msginfo != NULL) {
   1915 		if (src_msginfo)
   1916 			tmp_msginfo->folder = src_msginfo->folder;
   1917 		tmp_msginfo->plaintext_file = g_strdup(tmpfile);
   1918 	} else {
   1919 		g_warning("procmsg_msginfo_new_from_mimeinfo(): can't generate new msginfo");
   1920 	}
   1921 
   1922 	g_free(tmpfile);
   1923 
   1924 	return tmp_msginfo;
   1925 }
   1926 
   1927 static void item_has_queued_mails(FolderItem *item, gpointer data)
   1928 {
   1929 	gboolean *result = (gboolean *)data;
   1930 	if (*result == TRUE)
   1931 		return;
   1932 	if (folder_has_parent_of_type(item, F_QUEUE)) {
   1933 		if (item->total_msgs == 0)
   1934 			return;
   1935 		else {
   1936 			GSList *msglist = folder_item_get_msg_list(item);
   1937 			GSList *cur;
   1938 			for (cur = msglist; cur; cur = cur->next) {
   1939 				MsgInfo *msginfo = (MsgInfo *)cur->data;
   1940 				if (!MSG_IS_DELETED(msginfo->flags) &&
   1941 				    !MSG_IS_LOCKED(msginfo->flags)) {
   1942 					*result = TRUE;
   1943 					break;
   1944 				}
   1945 			}
   1946 			procmsg_msg_list_free(msglist);
   1947 		}
   1948 	}
   1949 }
   1950 
   1951 gboolean procmsg_have_queued_mails_fast (void)
   1952 {
   1953 	gboolean result = FALSE;
   1954 	folder_func_to_all_folders(item_has_queued_mails, &result);
   1955 	return result;
   1956 }
   1957 
   1958 static void item_has_trashed_mails(FolderItem *item, gpointer data)
   1959 {
   1960 	gboolean *result = (gboolean *)data;
   1961 	if (*result == TRUE)
   1962 		return;
   1963 	if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
   1964 		*result = TRUE;
   1965 }
   1966 
   1967 gboolean procmsg_have_trashed_mails_fast (void)
   1968 {
   1969 	gboolean result = FALSE;
   1970 	folder_func_to_all_folders(item_has_trashed_mails, &result);
   1971 	return result;
   1972 }
   1973