/*
 Copyright (C) 1999-2004 IC & S  dbmail@ic-s.nl

 This program is free software; you can redistribute it and/or 
 modify it under the terms of the GNU General Public License 
 as published by the Free Software Foundation; either 
 version 2 of the License, or (at your option) any later 
 version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * $Id: rfcmsg.c 2180 2006-06-18 06:39:48Z aaron $
 * function implementations for parsing an RFC822/MIME 
 * compliant mail message
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "rfcmsg.h"
#include "list.h"
#include "debug.h"
#include "mime.h"
#include "dbmsgbuf.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

/* 
 * frees all the memory associated with a msg
 */
void db_free_msg(mime_message_t * msg)
{
	struct element *tmp;

	if (!msg)
		return;

	/* free the children msg's */
	tmp = list_getstart(&msg->children);

	while (tmp) {
		db_free_msg((mime_message_t *) tmp->data);
		tmp = tmp->nextnode;
	}

	tmp = list_getstart(&msg->children);
	list_freelist(&tmp);

	tmp = list_getstart(&msg->mimeheader);
	list_freelist(&tmp);

	tmp = list_getstart(&msg->rfcheader);
	list_freelist(&tmp);

	memset(msg, 0, sizeof(*msg));
}


/* 
 * reverses the children lists of a msg
 */
void db_reverse_msg(mime_message_t * msg)
{
	struct element *tmp;

	if (!msg)
		return;

	/* reverse the children msg's */
	tmp = list_getstart(&msg->children);

	while (tmp) {
		db_reverse_msg((mime_message_t *) tmp->data);
		tmp = tmp->nextnode;
	}

	/* reverse this list */
	msg->children.start = dbmail_list_reverse(msg->children.start);

	/* reverse header items */
	msg->mimeheader.start = dbmail_list_reverse(msg->mimeheader.start);
	msg->rfcheader.start = dbmail_list_reverse(msg->rfcheader.start);
}

/*
 * db_fetch_headers()
 *
 * builds up an array containing message headers and the start/end position of the 
 * associated body part(s)
 *
 * creates a linked-list of headers found
 *
 * NOTE: there are no checks performed to verify that the indicated msg isn't expunged 
 *       (status STATUS_DELETE) or has been inserted completely. This should be done before calling
 *       this function (unless, of course, it is your intention to specifically parse an 
 *       incomplete message or an expunged one).
 *
 * returns:
 * -3 memory error
 * -2 dbase error
 * -1 parse error but msg is retrieved as plaintext
 *  0 success
 */
int db_fetch_headers(u64_t msguid, mime_message_t * msg)
{
	int result, level = 0, maxlevel = -1;

	if (db_init_fetch_message(msguid) != 1) {
		trace(TRACE_ERROR,
		      "db_fetch_headers(): could not init msgfetch\n");
		return -2;
	}

	result = db_start_msg(msg, NULL, &level, maxlevel);	/* fetch message */
	if (result < 0) {
		trace(TRACE_INFO,
		      "db_fetch_headers(): error fetching message, ID: %llu\n",
		      msguid);
		trace(TRACE_INFO,
		      "db_fetch_headers(): got error at level %d\n",
		      level);

		db_close_msgfetch();
		db_free_msg(msg);

		if (result < -1)
			return result;	/* memory/dbase error */

		/* 
		 * so an error occurred parsing the message. 
		 * try to lower the maxlevel of recursion
		 */

		for (maxlevel = level - 1; maxlevel >= 0; maxlevel--) {
			trace(TRACE_DEBUG,
			      "db_fetch_headers(): trying to fetch at maxlevel %d...\n",
			      maxlevel);

			if (db_init_fetch_message(msguid) != 1) {
				trace(TRACE_ERROR,
				      "db_fetch_headers(): could not init msgfetch\n");
				return -2;
			}

			level = 0;
			result = db_start_msg(msg, NULL, &level, maxlevel);

			db_close_msgfetch();

			if (result != -1)
				break;

			db_free_msg(msg);
		}

		if (result < -1) {
			db_free_msg(msg);
			return result;
		}

		if (result >= 0) {
			trace(TRACE_WARNING,
			      "db_fetch_headers(): succesfully recovered erroneous message %llu\n",
			      msguid);
			db_reverse_msg(msg);
			return 0;
		}


		/* ok still problems... try to make a message */
		if (db_init_fetch_message(msguid) != 1) {
			trace(TRACE_ERROR,
			      "db_fetch_headers(): could not init msgfetch\n");
			return -2;
		}

		result = db_parse_as_text(msg);
		if (result < 0) {
			/* probably some serious dbase error */
			trace(TRACE_ERROR,
			      "db_fetch_headers(): could not recover message as plain text\n");
			db_free_msg(msg);
			return result;
		}

		trace(TRACE_WARNING,
		      "db_fetch_headers(): message recovered as plain text\n");
		db_close_msgfetch();
		return -1;
	}

	db_reverse_msg(msg);

	db_close_msgfetch();
	return 0;
}


/*
 * db_start_msg()
 *
 * parses a msg; uses msgbuf_buf[] as data
 *
 * level & maxlevel are used to determine the max level of recursion (error-recovery)
 * level is raised before calling add_mime_children() except when maxlevel and level
 * are both zero, in that case the message is split in header/rest, add_mime_children
 * will not be called at all.
 *
 * returns the number of lines parsed or -1 on parse error, -2 on dbase error, -3 on memory error
 */
int db_start_msg(mime_message_t * msg, char *stopbound, int *level,
		 int maxlevel)
{
	int len, sblen, result, totallines = 0, nlines, hdrlines;
	struct mime_record *mr;
	char *newbound, *bptr;
	int continue_recursion = (maxlevel == 0 && *level == 0) ? 0 : 1;

	trace(TRACE_DEBUG, "db_start_msg(): starting, stopbound: '%s'\n",
	      stopbound ? stopbound : "<null>");

	list_init(&msg->children);
	msg->message_has_errors = (!continue_recursion);


	/* read header */
	if (db_update_msgbuf(MSGBUF_FORCE_UPDATE) == -1)
		return -2;

	if ((hdrlines =
	     mime_readheader(&msgbuf_buf[msgbuf_idx], &msgbuf_idx,
			     &msg->rfcheader, &msg->rfcheadersize)) < 0)
		return hdrlines;	/* error reading header */

	db_give_msgpos(&msg->bodystart);
	msg->rfcheaderlines = hdrlines;

	mime_findfield("content-type", &msg->rfcheader, &mr);
	if (continue_recursion &&
	    mr
	    && strncasecmp(mr->value, "multipart",
			   strlen("multipart")) == 0) {
		trace(TRACE_DEBUG,
		      "db_start_msg(): found multipart msg\n");

		/* multipart msg, find new boundary */
		for (bptr = mr->value; *bptr; bptr++)
			if (strncasecmp
			    (bptr, "boundary=",
			     sizeof("boundary=") - 1) == 0)
				break;

		if (!bptr) {
			trace(TRACE_WARNING,
			      "db_start_msg(): could not find a new msg-boundary\n");
			return -1;	/* no new boundary ??? */
		}

		bptr += sizeof("boundary=") - 1;
		if (*bptr == '\"') {
			bptr++;
			newbound = bptr;
			while (*newbound && *newbound != '\"')
				newbound++;
		} else {
			newbound = bptr;
			while (*newbound && !isspace(*newbound)
			       && *newbound != ';')
				newbound++;
		}

		len = newbound - bptr;
		if (!(newbound = (char *) dm_malloc(len + 1))) {
			trace(TRACE_ERROR,
			      "db_start_msg(): out of memory\n");
			return -3;
		}

		strncpy(newbound, bptr, len);
		newbound[len] = '\0';

		trace(TRACE_DEBUG,
		      "db_start_msg(): found new boundary: [%s], msgbuf_idx %llu\n",
		      newbound, msgbuf_idx);

		/* advance to first boundary */
		if (db_update_msgbuf(MSGBUF_FORCE_UPDATE) == -1) {
			trace(TRACE_ERROR,
			      "db_startmsg(): error updating msgbuf\n");
			dm_free(newbound);
			return -2;
		}

		while (msgbuf_buf[msgbuf_idx]) {
			if (strncmp
			    (&msgbuf_buf[msgbuf_idx], newbound,
			     strlen(newbound)) == 0)
				break;

			if (msgbuf_buf[msgbuf_idx] == '\n')
				totallines++;

			msgbuf_idx++;
		}

		if (!msgbuf_buf[msgbuf_idx]) {
			trace(TRACE_WARNING,
			      "db_start_msg(): unexpected end-of-data\n");
			dm_free(newbound);
			return -1;
		}

		msgbuf_idx += strlen(newbound);	/* skip the boundary */
		msgbuf_idx++;	/* skip \n */
		totallines++;	/* and count it */

		/* find MIME-parts */
		(*level)++;
		if ((nlines =
		     db_add_mime_children(&msg->children, newbound, level,
					  maxlevel)) < 0) {
			trace(TRACE_WARNING,
			      "db_start_msg(): error adding MIME-children\n");
			dm_free(newbound);
			return nlines;
		}
		(*level)--;
		totallines += nlines;

		/* skip stopbound if present */
		if (stopbound) {
			sblen = strlen(stopbound);
			msgbuf_idx += (2 + sblen);	/* double hyphen preceeds */
		}

		dm_free(newbound);
		newbound = NULL;

		if (msgbuf_idx > 0) {
			/* walk back because bodyend is inclusive */
			msgbuf_idx--;
			db_give_msgpos(&msg->bodyend);
			msgbuf_idx++;
		} else
			db_give_msgpos(&msg->bodyend);	/* this case should never happen... */


		msg->bodysize =
		    db_give_range_size(&msg->bodystart, &msg->bodyend);
		msg->bodylines = totallines;

		return totallines + hdrlines;	/* done */
	} else {
		/* single part msg, read untill stopbound OR end of buffer */
		trace(TRACE_DEBUG,
		      "db_start_msg(): found singlepart msg\n");

		if (stopbound) {
			sblen = strlen(stopbound);

			while (msgbuf_buf[msgbuf_idx]) {
				if (db_update_msgbuf(sblen + 3) == -1)
					return -2;

				if (msgbuf_buf[msgbuf_idx] == '\n')
					msg->bodylines++;

				if (msgbuf_buf[msgbuf_idx + 1] == '-'
				    && msgbuf_buf[msgbuf_idx + 2] == '-'
				    && strncmp(&msgbuf_buf[msgbuf_idx + 3],
					       stopbound, sblen) == 0) {
					db_give_msgpos(&msg->bodyend);
					msg->bodysize =
					    db_give_range_size(&msg->
							       bodystart,
							       &msg->
							       bodyend);

					msgbuf_idx++;	/* msgbuf_buf[msgbuf_idx] == '-' now */

					/* advance to after stopbound */
					msgbuf_idx += sblen + 2;	/* (add 2 cause double hyphen preceeds) */
					while (isspace
					       (msgbuf_buf[msgbuf_idx])) {
						if (msgbuf_buf[msgbuf_idx]
						    == '\n')
							totallines++;
						msgbuf_idx++;
					}

					trace(TRACE_DEBUG,
					      "db_start_msg(): stopbound reached\n");
					return (totallines +
						msg->bodylines + hdrlines);
				}

				msgbuf_idx++;
			}

			/* end of buffer reached, invalid message encountered: there should be a stopbound! */
			/* but lets pretend there's nothing wrong... */
			db_give_msgpos(&msg->bodyend);
			msg->bodysize =
			    db_give_range_size(&msg->bodystart,
					       &msg->bodyend);
			totallines += msg->bodylines;

			trace(TRACE_WARNING,
			      "db_start_msg(): no stopbound where expected...\n");

/*	  return -1;
*/
		} else {
			/* walk on till end of buffer */
			result = 1;
			while (1) {
				for (;
				     msgbuf_idx < msgbuf_buflen - 1
				     && msgbuf_buf[msgbuf_idx];
				     msgbuf_idx++)
					if (msgbuf_buf[msgbuf_idx] == '\n')
						msg->bodylines++;

				if (result == 0) {
					/* end of msg reached, one char left in msgbuf */
					if (msgbuf_buf[msgbuf_idx] == '\n')
						msg->bodylines++;

					break;
				}

				result =
				    db_update_msgbuf(MSGBUF_FORCE_UPDATE);
				if (result == -1)
					return -2;
			}

			db_give_msgpos(&msg->bodyend);
			msg->bodysize =
			    db_give_range_size(&msg->bodystart,
					       &msg->bodyend);
			totallines += msg->bodylines;
		}
	}

	trace(TRACE_DEBUG, "db_start_msg(): exit\n");

	return totallines;
}



/*
 * assume to enter just after a splitbound 
 * returns -1 on parse error, -2 on dbase error, -3 on memory error
 */
int db_add_mime_children(struct list *brothers, char *splitbound,
			 int *level, int maxlevel)
{
	mime_message_t part;
	struct mime_record *mr;
	int sblen, nlines, totallines = 0, len;
	u64_t dummy;
	char *bptr, *newbound;
	int continue_recursion = (maxlevel < 0
				  || *level < maxlevel) ? 1 : 0;

	trace(TRACE_DEBUG,
	      "db_add_mime_children(): starting, splitbound: '%s'\n",
	      splitbound);
	sblen = strlen(splitbound);

	do {
		db_update_msgbuf(MSGBUF_FORCE_UPDATE);
		memset(&part, 0, sizeof(part));
		part.message_has_errors = (!continue_recursion);

		/* should have a MIME header right here */
		if ((nlines =
		     mime_readheader(&msgbuf_buf[msgbuf_idx], &msgbuf_idx,
				     &part.mimeheader, &dummy)) < 0) {
			trace(TRACE_WARNING,
			      "db_add_mime_children(): error reading MIME-header\n");
			db_free_msg(&part);
			return nlines;	/* error reading header */
		}
		totallines += nlines;

		mime_findfield("content-type", &part.mimeheader, &mr);

		if (continue_recursion &&
		    mr
		    && strncasecmp(mr->value, "message/rfc822",
				   strlen("message/rfc822")) == 0) {
			trace(TRACE_DEBUG,
			      "db_add_mime_children(): found an RFC822 message\n");

			/* a message will follow */
			if ((nlines =
			     db_start_msg(&part, splitbound, level,
					  maxlevel)) < 0) {
				trace(TRACE_WARNING,
				      "db_add_mime_children(): error retrieving message\n");
				db_free_msg(&part);
				return nlines;
			}
			trace(TRACE_DEBUG,
			      "db_add_mime_children(): got %d newlines from start_msg()\n",
			      nlines);
			totallines += nlines;
			part.mimerfclines = nlines;
		} else if (continue_recursion &&
			   mr
			   && strncasecmp(mr->value, "multipart",
					  strlen("multipart")) == 0) {
			trace(TRACE_DEBUG,
			      "db_add_mime_children(): found a MIME multipart sub message\n");

			/* multipart msg, find new boundary */
			for (bptr = mr->value; *bptr; bptr++)
				if (strncasecmp
				    (bptr, "boundary=",
				     sizeof("boundary=") - 1) == 0)
					break;

			if (!bptr) {
				trace(TRACE_WARNING,
				      "db_add_mime_children(): could not find a new msg-boundary\n");
				db_free_msg(&part);
				return -1;	/* no new boundary ??? */
			}

			bptr += sizeof("boundary=") - 1;
			if (*bptr == '\"') {
				bptr++;
				newbound = bptr;
				while (*newbound && *newbound != '\"')
					newbound++;
			} else {
				newbound = bptr;
				while (*newbound && !isspace(*newbound)
				       && *newbound != ';')
					newbound++;
			}

			len = newbound - bptr;
			if (!(newbound = (char *) dm_malloc(len + 1))) {
				trace(TRACE_ERROR,
				      "db_add_mime_children(): out of memory\n");
				db_free_msg(&part);
				return -3;
			}

			strncpy(newbound, bptr, len);
			newbound[len] = '\0';

			trace(TRACE_DEBUG,
			      "db_add_mime_children(): found new boundary: [%s], msgbuf_idx %llu\n",
			      newbound, msgbuf_idx);


			/* advance to first boundary */
			if (db_update_msgbuf(MSGBUF_FORCE_UPDATE) == -1) {
				trace(TRACE_ERROR,
				      "db_add_mime_children(): error updating msgbuf\n");
				db_free_msg(&part);
				dm_free(newbound);
				return -2;
			}

			while (msgbuf_buf[msgbuf_idx]) {
				if (strncmp
				    (&msgbuf_buf[msgbuf_idx], newbound,
				     strlen(newbound)) == 0)
					break;

				if (msgbuf_buf[msgbuf_idx] == '\n') {
					totallines++;
					part.bodylines++;
				}

				msgbuf_idx++;
			}

			if (!msgbuf_buf[msgbuf_idx]) {
				trace(TRACE_WARNING,
				      "db_add_mime_children(): unexpected end-of-data\n");
				dm_free(newbound);
				db_free_msg(&part);
				return -1;
			}

			msgbuf_idx += strlen(newbound);	/* skip the boundary */
			msgbuf_idx++;	/* skip \n */
			totallines++;	/* and count it */
			part.bodylines++;
			db_give_msgpos(&part.bodystart);	/* remember position */

			(*level)++;
			if ((nlines =
			     db_add_mime_children(&part.children, newbound,
						  level, maxlevel)) < 0) {
				trace(TRACE_WARNING,
				      "db_add_mime_children(): error adding mime children\n");
				dm_free(newbound);
				db_free_msg(&part);
				return nlines;
			}
			(*level)--;

			dm_free(newbound);
			newbound = NULL;
			msgbuf_idx += sblen + 2;	/* skip splitbound */

			if (msgbuf_idx > 0) {
				/* walk back because bodyend is inclusive */
				msgbuf_idx--;
				db_give_msgpos(&part.bodyend);
				msgbuf_idx++;
			} else
				db_give_msgpos(&part.bodyend);	/* this case should never happen... */


			part.bodysize =
			    db_give_range_size(&part.bodystart,
					       &part.bodyend);
			part.bodylines += nlines;
			totallines += nlines;
		} else {
			trace(TRACE_DEBUG,
			      "db_add_mime_children(): expecting body data...\n");

			/* just body data follows, advance to splitbound */
			db_give_msgpos(&part.bodystart);

			while (msgbuf_buf[msgbuf_idx]) {
				if (db_update_msgbuf(sblen + 3) == -1) {
					db_free_msg(&part);
					return -2;
				}

				if (msgbuf_buf[msgbuf_idx] == '\n')
					part.bodylines++;

				if (msgbuf_buf[msgbuf_idx + 1] == '-'
				    && msgbuf_buf[msgbuf_idx + 2] == '-'
				    && strncmp(&msgbuf_buf[msgbuf_idx + 3],
					       splitbound, sblen) == 0)
					break;

				msgbuf_idx++;
			}

			/* at this point msgbuf_buf[msgbuf_idx] is either
			 * 0 (end of data) -- invalid message!
			 * or the character right before '--<splitbound>'
			 */

			totallines += part.bodylines;

			if (!msgbuf_buf[msgbuf_idx]) {
				trace(TRACE_WARNING,
				      "db_add_mime_children(): unexpected end of data\n");
				db_free_msg(&part);
				return -1;	/* ?? splitbound should follow */
			}

			db_give_msgpos(&part.bodyend);
			part.bodysize =
			    db_give_range_size(&part.bodystart,
					       &part.bodyend);

			msgbuf_idx++;	/* msgbuf_buf[msgbuf_idx] == '-' after this statement */

			msgbuf_idx += sblen + 2;	/* skip the boundary & double hypen */
		}

		/* add this part to brother list */
		if (list_nodeadd(brothers, &part, sizeof(part)) == NULL) {
			trace(TRACE_WARNING,
			      "db_add_mime_children(): could not add node\n");
			db_free_msg(&part);
			return -3;
		}

		/* if double hyphen ('--') follows we're done */
		if (msgbuf_buf[msgbuf_idx] == '-'
		    && msgbuf_buf[msgbuf_idx + 1] == '-') {
			trace(TRACE_DEBUG,
			      "db_add_mime_children(): found end after boundary [%s],\n",
			      splitbound);
			trace(TRACE_DEBUG,
			      "                        followed by [%.*s],\n",
			      48, &msgbuf_buf[msgbuf_idx]);

			msgbuf_idx += 2;	/* skip hyphens */

			/* probably some newlines will follow (not specified but often there) */
			while (msgbuf_buf[msgbuf_idx] == '\n') {
				totallines++;
				msgbuf_idx++;
			}

			return totallines;
		}

		if (msgbuf_buf[msgbuf_idx] == '\n') {
			totallines++;
			msgbuf_idx++;	/* skip the newline itself */
		}
	}
	while (msgbuf_buf[msgbuf_idx]);

	trace(TRACE_WARNING,
	      "db_add_mime_children(): sudden end of message\n");
	return totallines;

/*  trace(TRACE_ERROR,"db_add_mime_children(): invalid message (no ending boundary found)\n");
  return -1;
*/
}


/*
 * db_parse_as_text()
 * 
 * parses a message as a block of plain text; an explaining header is created
 * note that this will disturb the length calculations...
 * this function is called when normal parsing fails.
 * 
 * returns -1 on dbase failure, -2 on memory error
 */
int db_parse_as_text(mime_message_t * msg)
{
	int result;
	struct mime_record mr;
	struct element *el = NULL;
	field_t postmaster;

	memset(msg, 0, sizeof(*msg));

	strcpy(mr.field, "subject");
	strcpy(mr.value,
	       "dbmail IMAP server info: this message could not be parsed");
	el = list_nodeadd(&msg->rfcheader, &mr, sizeof(mr));
	if (!el)
		return -3;

	strcpy(mr.field, "from");
	GetConfigValue("POSTMASTER", "DBMAIL", postmaster);
	strncpy(mr.value, postmaster, MIME_VALUE_MAX - 1);
	el = list_nodeadd(&msg->rfcheader, &mr, sizeof(mr));
	if (!el)
		return -3;

	msg->rfcheadersize =
	    strlen
	    ("subject: dbmail IMAP server info: this message could not be parsed\r\n")
	    + strlen("from: imapserver@dbmail.org\r\n");
	msg->rfcheaderlines = 4;

	db_give_msgpos(&msg->bodystart);

	/* walk on till end of buffer */
	result = 1;
	while (1) {
		for (; msgbuf_idx < msgbuf_buflen - 1; msgbuf_idx++)
			if (msgbuf_buf[msgbuf_idx] == '\n')
				msg->bodylines++;

		if (result == 0) {
			/* end of msg reached, one char left in msgbuf_buf */
			if (msgbuf_buf[msgbuf_idx] == '\n')
				msg->bodylines++;

			break;
		}

		result = db_update_msgbuf(MSGBUF_FORCE_UPDATE);
		if (result == -1)
			return -2;
	}

	db_give_msgpos(&msg->bodyend);
	msg->bodysize = db_give_range_size(&msg->bodystart, &msg->bodyend);

	return 0;
}


/*
 * db_msgdump()
 *
 * dumps a message to stderr
 * returns the size (in bytes) that the message occupies in memory
 */
int db_msgdump(mime_message_t * msg, u64_t msguid, int level)
{
	struct element *curr;
	struct mime_record *mr;
	char *spaces;
	int size = sizeof(mime_message_t);

	if (level < 0)
		return 0;

	if (!msg) {
		trace(TRACE_DEBUG, "db_msgdump: got null\n");
		return 0;
	}

	spaces = (char *) dm_malloc(3 * level + 1);
	if (!spaces)
		return 0;

	memset(spaces, ' ', 3 * level);
	spaces[3 * level] = 0;


	trace(TRACE_DEBUG, "%sMIME-header: \n", spaces);
	curr = list_getstart(&msg->mimeheader);
	if (!curr)
		trace(TRACE_DEBUG, "%s%snull\n", spaces, spaces);
	else {
		while (curr) {
			mr = (struct mime_record *) curr->data;
			trace(TRACE_DEBUG, "%s%s[%s] : [%s]\n", spaces,
			      spaces, mr->field, mr->value);
			curr = curr->nextnode;
			size += sizeof(struct mime_record);
		}
	}
	trace(TRACE_DEBUG, "%s*** MIME-header end\n", spaces);

	trace(TRACE_DEBUG, "%sRFC822-header: \n", spaces);
	curr = list_getstart(&msg->rfcheader);
	if (!curr)
		trace(TRACE_DEBUG, "%s%snull\n", spaces, spaces);
	else {
		while (curr) {
			mr = (struct mime_record *) curr->data;
			trace(TRACE_DEBUG, "%s%s[%s] : [%s]\n", spaces,
			      spaces, mr->field, mr->value);
			curr = curr->nextnode;
			size += sizeof(struct mime_record);
		}
	}
	trace(TRACE_DEBUG, "%s*** RFC822-header end\n", spaces);

	trace(TRACE_DEBUG, "%s*** Body range:\n", spaces);
	trace(TRACE_DEBUG,
	      "%s%s(%llu, %llu) - (%llu, %llu), size: %llu, newlines: %llu\n",
	      spaces, spaces, msg->bodystart.block, msg->bodystart.pos,
	      msg->bodyend.block, msg->bodyend.pos, msg->bodysize,
	      msg->bodylines);

/* Enable this debug code to see what's happening.
 * trace(TRACE_DEBUG,"body: \n");
 * db_dump_range(msg->bodystart, msg->bodyend, msguid);
 * trace(TRACE_DEBUG,"*** body end\n");
 */
	trace(TRACE_DEBUG, "%sChildren of this msg:\n", spaces);

	curr = list_getstart(&msg->children);
	while (curr) {
		size +=
		    db_msgdump((mime_message_t *) curr->data, msguid,
			       level + 1);
		curr = curr->nextnode;
	}
	trace(TRACE_DEBUG, "%s*** child list end\n", spaces);

	dm_free(spaces);
	return size;
}


syntax highlighted by Code2HTML, v. 0.9.1