/* $Id: mime.cpp,v 1.34 2004/01/02 03:54:01 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of a library used by the Archimedes email client     * 
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   This program is free software; you can redistribute it and/or modify      *
 *   it under the terms of the GNU Library 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 Library General Public License for more details.                      *
 *                                                                             *
 *   You should have received a copy of the GNU Library General Public License *
 *   along with this program; if not, write to the Free Software               *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 ******************************************************************************/


#include <fmail.h>
#include <umail.h>
#include <cfgfile.h>

#define XF_PREAMBLE "This message is in MIME format"
#define XF_EPILOG   "End of MIME message"

struct _mime_mailcap mailcap[MAX_MCAP_SIZE] = {
	/* text */
	/* 00 */ {CTYPE_TEXT, "text", CSUBTYPE_PLAIN, "plain", text_view,
		text_print, NULL, NULL, "txt", CE_7BIT},
	/* 01 */ {CTYPE_TEXT, "text", CSUBTYPE_RTF, "richtext", text_view,
		text_print, NULL, NULL, "rtf", CE_7BIT},
	/* 02 */ {CTYPE_TEXT, "text", CSUBTYPE_HTML, "html", NULL, text_print,
		NULL, NULL, "htm", CE_7BIT},
	/* 03 */ {CTYPE_TEXT, "text", CSUBTYPE_DEFAULT, "*", text_view,
		text_print, NULL, NULL, "", CE_7BIT},
	/* multipart */
	/* 04 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_MIXED, "mixed",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* 05 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_ALTERNATIVE,
		"alternative", digest_view, NULL, process_multipart, NULL,
		"", CE_NONE},
	/* 06 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_DIGEST, "digest",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* 07 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_PARALLEL, "parallel",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* 08 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_REPORT, "report",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* 09 */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_ENCRYPTED,
		"encrypted", digest_view, NULL, process_multipart, NULL, "",
		CE_NONE},
	/* 0a */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_SIGNED, "signed",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* 0b */ {CTYPE_MULTIPART, "multipart", CSUBTYPE_DEFAULT, "*",
		digest_view, NULL, process_multipart, NULL, "", CE_NONE},
	/* message */
	/* 0c */ {CTYPE_MESSAGE, "message", CSUBTYPE_RFC822, "rfc822",
		msg_view, text_print, NULL, NULL, "", CE_7BIT},
	/* 0d */ {CTYPE_MESSAGE, "message", CSUBTYPE_PARTIAL, "partial",
		assemble_partial, NULL, NULL, NULL, "", CE_7BIT},
	/* 0e */ {CTYPE_MESSAGE, "message", CSUBTYPE_EXTBODY, "external-body",
		view_external, NULL, NULL, NULL, "", CE_7BIT},
	/* 0f */ {CTYPE_MESSAGE, "message", CSUBTYPE_DEFAULT, "*", msg_view,
		NULL, NULL, NULL, "", CE_7BIT},
	/* application */
	/* 10 */ {CTYPE_APPLICATION, "application", CSUBTYPE_OSTREAM,
		"octet-stream", NULL, NULL, NULL, NULL, "exe", CE_BASE64},
	/* 11 */ {CTYPE_APPLICATION, "application", CSUBTYPE_PSCRIPT,
		"PostScript", NULL, NULL, NULL, NULL, "ps", CE_BASE64},
	/* 12 */ {CTYPE_APPLICATION, "application", CSUBTYPE_PGP, "pgp",
		pgp_view, NULL, NULL, NULL, "pgp", CE_7BIT},
	/* 13 */ {CTYPE_APPLICATION, "application", CSUBTYPE_PGPSIG,
		"pgp-signature", pgpsig_view, NULL, NULL, NULL, "pgp",
		CE_7BIT},
	/* 14 */ {CTYPE_APPLICATION, "application", CSUBTYPE_PGPENC,
		"pgp-encrypted", pgpenc_view, NULL, NULL, NULL, "pgp",
		CE_7BIT},
	/* 15 */ {CTYPE_APPLICATION, "application", CSUBTYPE_PGPKEYS,
		"pgp-keys", pgpkeys_view, NULL, NULL, NULL, "pgp", CE_7BIT},
	/* image */
	/* 16 */ {CTYPE_IMAGE, "image", CSUBTYPE_GIF, "gif", NULL, NULL, NULL,
		NULL, "gif", CE_BASE64},
	/* 17 */ {CTYPE_IMAGE, "image", CSUBTYPE_JPEG, "jpeg", NULL, NULL,
		NULL, NULL, "jpg", CE_BASE64},
	/* video */
	/* 18 */ {CTYPE_VIDEO, "video", CSUBTYPE_MPEG, "mpeg", NULL, NULL,
		NULL, NULL, "mpg", CE_BASE64},
	/* audio */
	/* 19 */ {CTYPE_AUDIO, "audio", CSUBTYPE_BASIC, "basic", mime_play,
		NULL, NULL, NULL, "au", CE_BASE64},
	/* misc */
	/* 1a */ {CTYPE_DEFAULT, "*", CSUBTYPE_DEFAULT, "*", text_view, NULL,
		NULL, NULL, "", CE_NONE},
	/* 1b */ {CTYPE_UNSUPPORTED, "", CSUBTYPE_UNSUPPORTED, "", NULL, NULL,
		NULL, NULL, "", CE_NONE}
};

const char sevenbit_chars[] = "\r\n\t";

extern cfgfile Config;

char * enc_buf;
unsigned long enc_buf_len = 0;
char * dec_buf;
unsigned long dec_buf_len = 0;

int qprt_header = 0;        /* indicates that quoted-printable is used on header */

const char base64_albet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

const char hex_char1[] = "0123456789ABCDEF";
const char hex_char2[] = "0123456789abcdef";

#define GLOBAL_MCAP "/etc/xfmime"   /* Global XFMail mailcap */

struct _mime_encoding supp_encodings[] = {
	{CE_NONE, "none", dumb_encode, dumb_decode, 80},
	{CE_7BIT, "7bit", sevenbit_encode, sevenbit_decode, 80},
	{CE_QPRT, "quoted-printable", qprt_encode, qprt_decode, 76},
	{CE_BASE64, "base64", base64_encode, base64_decode, 76},
	{CE_8BIT, "8bit", eightbit_encode, eightbit_decode, 80},
	{CE_BINARY, "binary", dumb_encode, dumb_decode, 255},
	{CE_UNSUPPORTED, "unknown", dumb_encode, dumb_decode, 90}
};

struct _mime_charset supp_charsets[MAX_CHARSETS] = {
	{ US_ASCII, "us-ascii", "English", NULL, NULL, 1 },
	{ ISO_8859_1, "iso-8859-1", "Western European", NULL, NULL, 1 },
	{ ISO_8859_2, "iso-8859-2", "Central European", NULL, NULL, 1 },
	{ ISO_8859_3, "iso-8859-3", "Esperanto", NULL, NULL, 1 },
	{ ISO_8859_4, "iso-8859-4", "old Baltic", NULL, NULL, 1 },
	{ ISO_8859_5, "iso-8859-5", "Cyrillic", NULL, NULL, 1 },
	{ ISO_8859_6, "iso-8859-6", "Arabic", NULL, NULL, 1 },
	{ ISO_8859_7, "iso-8859-7", "Greek", NULL, NULL, 1 },
	{ ISO_8859_8, "iso-8859-8", "Hebrew", hebrew_conv, NULL, 1 },
	{ ISO_8859_9, "iso-8859-9", "Turkish", NULL, NULL, 1 },
	{ ISO_8859_10, "iso-8859-10", "Nordic", NULL, NULL, 1 },
	{ ISO_8859_13, "iso-8859-13", "Baltic", NULL, NULL, 1 },
	{ ISO_8859_14, "iso-8859-14", "Celtic", NULL, NULL, 1 },
	{ ISO_8859_15, "iso-8859-15", "Western European", NULL, NULL, 1 },
	{ ISO_8859_1, "iso8859-1", "Western European", NULL, NULL, 1 },
	{ ISO_8859_2, "iso8859-2", "Central European", NULL, NULL, 1 },
	{ ISO_8859_3, "iso8859-3", "Esperanto", NULL, NULL, 1 },
	{ ISO_8859_4, "iso8859-4", "old Baltic", NULL, NULL, 1 },
	{ ISO_8859_5, "iso8859-5", "Cyrillic", NULL, NULL, 1 },
	{ ISO_8859_6, "iso8859-6", "Arabic", NULL, NULL, 1 },
	{ ISO_8859_7, "iso8859-7", "Greek", NULL, NULL, 1 },
	{ ISO_8859_8, "iso8859-8", "Hebrew", hebrew_conv, NULL, 1 },
	{ ISO_8859_9, "iso8859-9", "Turkish", NULL, NULL, 1 },
	{ ISO_8859_10, "iso8859-10", "Nordic", NULL, NULL, 1 },
	{ ISO_8859_13, "iso8859-13", "Baltic", NULL, NULL, 1 },
	{ ISO_8859_14, "iso8859-14", "Celtic", NULL, NULL, 1 },
	{ ISO_8859_15, "iso8859-15", "Western European", NULL, NULL, 1 },
	{ KOI8R, "KOI8-R", "Russian", NULL, NULL, 1 },
	{ CHAR_UNKNOWN, "unknown", NULL, NULL, NULL, 1 }
};

u_int def_charset = DEFAULT_CHARSET;
u_int def_encoding = DEFAULT_ENCODING;

int get_hex(char *hex) {
	u_int h1, h2;
	char *p;

	if((p = strchr(hex_char1, hex[0])) == NULL) {
		if((p = strchr(hex_char2, hex[0])) == NULL)
			return -1;
		else
			h1 = p - hex_char2;
	} else
		h1 = p - hex_char1;

	h1 = (h1 << 4) & 0xf0;

	if((p = strchr(hex_char1, hex[1])) == NULL) {
		if((p = strchr(hex_char2, hex[1])) == NULL)
			return -1;
		else
			h2 = p - hex_char2;
	} else
		h2 = p - hex_char1;

	h2 = h2 & 0x0f;

	return(h1 | h2);
}

void deinit_coders() {
	free(dec_buf);
	free(enc_buf);
	enc_buf_len = dec_buf_len = 0;
}

void init_coders() {
	dec_buf = (char*) malloc(sizeof(char*) * 512);
	enc_buf = (char*) malloc(sizeof(char*) * 512);
	enc_buf_len = dec_buf_len = 512;
}

void decode_init(int * n, char * str) {
	int nlocal;

	if(!str)
		return;

	if(*n < strlen(str)) {
		nlocal = strlen(str);
	} else {
		nlocal = *n;
	}

	if((nlocal > 0) && (nlocal > dec_buf_len)) {
		//fprintf(stderr, "n = %i  dec_buf_len = %ld\n",*n,dec_buf_len);
		dec_buf = (char*) realloc((void*)dec_buf, sizeof(char*) * nlocal);
		dec_buf_len = nlocal;
	}
}

void encode_init(int n, char * str) {
	if((n > enc_buf_len) && (n > 0)) {
		//fprintf(stderr, "n = %i  enc_buf_len = %ld\n",n,enc_buf_len);
		enc_buf = (char*) realloc((void*)enc_buf, sizeof(char*)*n);
		enc_buf_len = n;
	}
}

char *dumb_encode(char *str, int n) {

	if(!str || (n == 0))
		return "";

	encode_init(n,str);

	memcpy(enc_buf,str,sizeof(char)*n);
	enc_buf[n] = '\0';
	return enc_buf;
}

char *dumb_decode(char *str, int *n) {
	if(!str)
		return "";

	*n = strlen(str);
	decode_init(n,str);

	memcpy(dec_buf,str,sizeof(char)*(*n));
	dec_buf[*n] = '\0';

	return dec_buf;
}

char *sevenbit_encode(char *str, int n) {
	int i, k;

	if(!str || (n == 0))
		return "";

	encode_init(n,str);

	k = 0;
	for(i = 0; i < n; i++) {
		if(str[i] == '\0')
			continue;

		enc_buf[k] = str[i] & 0x7f;
		if((enc_buf[k] < 32) && (!strchr(sevenbit_chars, enc_buf[k])))
			enc_buf[k] = ' ';

		k++;
	}

	enc_buf[k] = '\0';
	return enc_buf;
}

char *sevenbit_decode(char *str, int *n) {

	if(!str)
		return "";

	*n = strlen(str);
	decode_init(n,str);

	memcpy(dec_buf,str,sizeof(char)*(*n));
	dec_buf[*n] = '\0';
	return dec_buf;
}

char *eightbit_encode(char *str, int n) {

	if(!str || (n == 0))
		return "";

	encode_init(n,str);

	memcpy(enc_buf,str,sizeof(char)*(n));

	enc_buf[n] = '\0';
	return enc_buf;
}

char *eightbit_decode(char *str, int *n) {

	if(!str)
		return "";

	*n = strlen(str);
	decode_init(n,str);

	memcpy(dec_buf,str,sizeof(char)*(*n));
	dec_buf[*n] = '\0';
	return dec_buf;
}

char *qprt_encode(char *str, int n) {
	static int len;
	int i, k;
	char hex[10];
	int str_len = supp_encodings[QPRT_ENCODING].str_len;

	if(!str || (n == 0)) {
		len = 0;
		return "";
	}

	encode_init(n,str);

	k = 0;
	for(i = 0; i < n; i++) {

		if(k >= (enc_buf_len - 3)) {
			enc_buf = (char*) realloc((void*)enc_buf, sizeof(char*)*n);
			enc_buf_len = n;
		}

		switch(str[i]) {
			case '\t':
			case ' ':
				if(qprt_header && (str[i] == ' ')) {
					enc_buf[k] = '_';
					len++;
					break;
				}

				if((i <= (n - 1)) && (str[i + 1] != '\n')) {
					len++;
					enc_buf[k] = str[i];
					if(!qprt_header && (len >= (str_len - 1))) {
						enc_buf[k + 1] = '=';
						enc_buf[k + 2] = '\n';
						k += 2;
						len = 0;
					}
				} else {
					if(!qprt_header && (len >= (str_len - 4))) {
						enc_buf[k] = '=';
						enc_buf[k + 1] = '\n';
						k += 2;
						len = 0;
					}

					sprintf(hex, "=%02X", str[i]);
					enc_buf[k] = hex[0];
					enc_buf[k + 1] = hex[1];
					enc_buf[k + 2] = hex[2];
					k += 2;
					len += 3;
				}
				break;

			case '\n':
				enc_buf[k] = str[i];
				len = 0;
				break;

			default:
				if(((str[i] >= 33) && (str[i] <= 60))
				   || ((str[i] >= 62) && (str[i] <= 126))) {
					enc_buf[k] = str[i];
					len++;
					if(!qprt_header && ((len >= (str_len - 1)))
					   && (str[i + 1] != '\n')) {
						enc_buf[k + 1] = '=';
						enc_buf[k + 2] = '\n';
						k += 2;
						len = 0;
					}
				} else {
					if(!qprt_header && (len >= (str_len - 4))) {
						enc_buf[k] = '=';
						enc_buf[k + 1] = '\n';
						k += 2;
						len = 0;
					}

					sprintf(hex, "=%02X", (unsigned char) str[i]);
					enc_buf[k] = hex[0];
					enc_buf[k + 1] = hex[1];
					enc_buf[k + 2] = hex[2];
					k += 2;
					len += 3;
				}
				break;
		}
		k++;
	}

	enc_buf[k] = '\0';
	return enc_buf;
}

char *qprt_decode(char *str, int *n) {
	char *p;
	int i;

	if(!str)
		return "";


	*n = 0;
	decode_init(n,str);
	p = str;
	while(*p != '\0') {
		switch(*p) {
			case '=':
				p++;
				if((*p == '\n') || (*p == '\r'))
					break;

				if(*p == '\0') {
					p--;
					break;
				}

				if((p[1] == '\0') || ((i = get_hex(p)) == -1)) {
					p--;
					i = '=';
				} else
					p++;

				dec_buf[*n] = i;
				(*n)++;
				break;

			case '_':
				if(qprt_header) {
					dec_buf[*n] = ' ';
					(*n)++;
					break;
				}

			default:
				//fprintf(stderr, "buf size: %i str size: %i cur: %i\n",dec_buf_len, strlen(str), *n);
				dec_buf[*n] = *p;
				(*n)++;
				break;
		}

		p++;
	}

	dec_buf[*n] = '\0';

	return dec_buf;
}

char *base64_encode_3(char *triplet, int n) {
	char src[4];
	static char encoded[5];
	int i, code, len;

	if(triplet == NULL)
		return NULL;

	if((n < 1) || (n > 3))
		return NULL;

	encode_init(n,triplet);

	src[0] = src[1] = src[2] = src[3] = '\0';
	encoded[4] = '\0';

	i = 0;
	len = n;
	while(n) {
		src[i] = *triplet;
		triplet++;
		i++;
		n--;
	}

	code = (src[0] >> 2) & 0x3f;
	encoded[0] = base64_albet[code];

	code = ((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0f);
	encoded[1] = base64_albet[code];

	code = ((src[1] << 2) & 0x3c) | ((src[2] >> 6) & 0x03);
	encoded[2] = base64_albet[code];

	code = (src[2] & 0x3f);
	encoded[3] = base64_albet[code];

	switch(len) {
		case 1:
			encoded[2] = '=';
			encoded[3] = '=';
			break;

		case 2:
			encoded[3] = '=';
			break;
	}


	return encoded;
}

char *base64_decode_4(char *four, int *len) {
	static char decoded[4];
	int i1, i2, i3, i4;
	char *p;

	decoded[0] = decoded[1] = decoded[2] = decoded[3] = '\0';
	*len = 3;

	if(strlen(four) != 4)
		return NULL;

	if((p = strchr(base64_albet, four[0])) == NULL)
		return NULL;

	decode_init(len,four);

	i1 = (p - base64_albet);

	if((p = strchr(base64_albet, four[1])) == NULL)
		return NULL;

	i2 = (p - base64_albet);

	if((p = strchr(base64_albet, four[2])) == NULL)
		return NULL;

	i3 = (p - base64_albet);

	if((p = strchr(base64_albet, four[3])) == NULL)
		return NULL;

	i4 = (p - base64_albet);

	if((i1 > 63) || (i2 > 63) || (i3 > 64) || (i4 > 64))
		return NULL;

	decoded[0] = ((i1 << 2) & 0xfc);
	decoded[0] = (decoded[0] | ((i2 >> 4) & 0x03));

	decoded[1] = ((i2 << 4) & 0xf0);
	decoded[1] = (decoded[1] | ((i3 >> 2) & 0x0f));

	decoded[2] = ((i3 << 6) & 0xc0);
	decoded[2] = (decoded[2] | (i4 & 0x3f));

	if((i3 == 64) && (i4 == 64)) {
		*len = 1;
		decoded[1] = '\0';
	} else {
		if(i4 == 64) {
			*len = 2;
			decoded[2] = '\0';
		}
	}

	return decoded;
}

char *base64_encode(char *str, int len) {
	static char triplet[4];
	static int trlen;

	char *p, *d;

	encode_init(len,str);

	if(!str || (len == 0)) {
		if(trlen) {
			p = base64_encode_3(triplet, trlen);
			if(p == NULL) {
				display_msg(MSG_WARN, "MIME", "Failed to encode base64");
				return NULL;
			}

			trlen = 0;
			return p;
		}

		trlen = 0;
		return "";
	}

	enc_buf[0] = '\0';
	p = str;

	while(len) {
		if(trlen == 3) {
			d = base64_encode_3(triplet, trlen);
			strcat(enc_buf, d);

			trlen = 0;

			if(strlen(enc_buf) >= (sizeof(enc_buf) - 5))
				return NULL;

		}

		triplet[trlen] = *p;
		trlen++;
		p++;
		len--;
	}

	return enc_buf;
}

char *base64_decode(char *str, int *len) {
	static char four[5];
	static int fourlen;

	char *p, *d;
	int i;

	*len = 0;
	decode_init(len,str);

	if(str == NULL) {
		if(fourlen) {
			fourlen = 0;
			return NULL;
		}

		fourlen = 0;
		return "";
	}

	p = str;

	while(1) {
		if(fourlen == 4) {

			four[4] = '\0';
			if((d = base64_decode_4(four, &i)) != NULL) {
				int k = 0;

				while(i) {
					dec_buf[*len] = d[k];
					k++;
					(*len)++;
					i--;
				}
			} else
				return NULL;

			fourlen = 0;
		}

		if(*len >= (sizeof(dec_buf) - 5)) {
			dec_buf = (char*) realloc((void*)dec_buf, sizeof(char*)* (*len));
			dec_buf_len = *len;
		}

		if(*p == '\0')
			break;

		if(strchr(base64_albet, *p) == NULL) {
			p++;
			continue;
		}

		four[fourlen] = *p;
		fourlen++;
		p++;
	}

	dec_buf[*len] = '\0';
	return dec_buf;
}

char *get_fld_param(struct _head_field *fld, char *pname) {
	static char pbody[127];
	char *p, *p1, *p2, c;
	int len, len1;

	if(!pname || !fld)
		return NULL;

	if(!fld->f_line || (strlen(pname) <= 1))
		return NULL;

	p = fld->f_line;
	len = strlen(pname);

	do {
		c = *p;
		switch(c) {
			case '"':
			case '\'':
				if((p != fld->f_line) && (*(p - 1) == '\\')) {
					p++;
					break;
				}

				p++;
				p1 = p;
				p2 = p;

				while(p1) {
					if(((p1 = strchr(p2, c)) != NULL) && (*(p1 - 1) != '\\')) {
						p = p1 + 1;
						break;
					}

					if(p1)
						p2 = p1 + 1;
				}
				break;

			case ';':
				p++;
			default:
				while((*p == ' ') || (*p == 0x09) || (*p == ';'))
					p++;
				if(strncasecmp(p, pname, len))
					break;
				p += len;
				while((*p == ' ') || (*p == 0x09))
					p++;
				if(*p != '=') {
					if((*p == ';') || (*p == 0x00))
						return "exists";
					break;
				}
				p++;
				while((*p == ' ') || (*p == 0x09))
					p++;
				if((*p == '\'') || (*p == '"')) {
					c = *p;
					p++;
					p1 = p;
					p2 = p;
					len1 = -1;

					while(p1) {
						if(((p1 = strchr(p2, c)) != NULL) &&
						   (*(p1 - 1) != '\\')) {
							len1 = p1 - p;
							break;
						}

						if(p1)
							p2 = p1 + 1;
					}
					if(len1 < 0)
						len1 = strlen(p);
				} else {
					if((p1 = strchr(p, ';')) != NULL)
						len1 = p1 - p;
					else
						len1 = strlen(p);
					while((p[len1 - 1] == ' ') || (p[len1 - 1] == 0x09))
						len1--;
				}

				if(len1 > sizeof(pbody) - 1)
					len1 = sizeof(pbody) - 1;
				snprintf(pbody, len1 + 1, "%s", p);

				return pbody;
				break;
		}
	} while((p = strpbrk(p, "'\";")) != NULL);

	return NULL;
}

u_int get_mime_version(struct _mail_msg * msg) {
	struct _head_field *hf;
	u_int vers;
	char cvers[4];

	if((hf = find_field(msg, MIME_VERSION)) == NULL)
		return MIME_VERS_SUPP;

	if(hf->f_line[1] != '.')
		return 0;

	cvers[2] = '\0';
	cvers[0] = hf->f_line[0];
	cvers[1] = hf->f_line[2];
	vers = atoi(cvers);

	return vers;
}

int is_mime(struct _mail_msg *msg) {
	u_int vers = get_mime_version(msg);

	if(vers == 0)
		return 0;

	if(vers > MIME_VERS_SUPP) {
		display_msg(MSG_WARN, "MIME", "Unsupported MIME version %d", vers);
		return 0;
	}

	if(find_field(msg, MIME_C_TYPE))
		return 1;

	return 0;
}

void add_mailcap(struct _mime_mailcap *mcap) {
	int i;

	if(!mcap)
		return;

	i = 0;
	while(mailcap[i].type_code != CTYPE_UNSUPPORTED) {
		if(!strcasecmp(mailcap[i].type_text, mcap->type_text) &&
		   !strcasecmp(mailcap[i].subtype_text, mcap->subtype_text)) {
			if(mailcap[i].process)
				display_msg(MSG_WARN, "MIME", "%s/%s already exists",
							mcap->type_text, mcap->subtype_text);
			else {
				if(&mailcap[i] == mcap)
					return;

				if(mailcap[i].ext_mcap)
					free(mailcap[i].ext_mcap);

				if(mcap->ext_mcap)
					mailcap[i].ext_mcap = strdup(mcap->ext_mcap);
				else
					mailcap[i].ext_mcap = NULL;
			}
			return;
		}

		i++;
	}

	if(i >= 126)
		return;

	mailcap[i].type_code = CTYPE_EXTERNAL;
	if(!strcmp(mcap->type_text, "*"))
		mailcap[i].subtype_code = CSUBTYPE_DEFAULT;
	else
		mailcap[i].subtype_code = CSUBTYPE_EXTERNAL;

	snprintf(mailcap[i].type_text, sizeof(mailcap[i].type_text), "%s", mcap->type_text);
	snprintf(mailcap[i].subtype_text, sizeof(mailcap[i].subtype_text), "%s", mcap->subtype_text);

	mailcap[i].view = NULL;
	mailcap[i].print = NULL;
	mailcap[i].process = NULL;
	mailcap[i].encode = CE_BASE64;

	if(mcap->ext_mcap)
		mailcap[i].ext_mcap = strdup(mcap->ext_mcap);
	else
		mailcap[i].ext_mcap = NULL;

	if(strlen(mcap->ext) > 1)
		snprintf(mailcap[i].ext, sizeof(mailcap[i].ext), "%s", mcap->ext);
	else
		mailcap[i].ext[0] = '\0';

	i++;
	mailcap[i].type_code = CTYPE_UNSUPPORTED;
	mailcap[i].subtype_code = CSUBTYPE_UNSUPPORTED;

	return;
}

struct _mime_mailcap *copy_mailcap(struct _mime_mailcap *mcap) {
	struct _mime_mailcap *newmcap;

	if(!mcap)
		return NULL;

	if(
	  (newmcap =
	   (struct _mime_mailcap *) malloc(sizeof(struct _mime_mailcap))) ==
	  NULL) {
		display_msg(MSG_WARN, "MIME", "malloc failed");
		return NULL;
	}

	newmcap->type_code = mcap->type_code;
	snprintf(newmcap->type_text, sizeof(newmcap->type_text), "%s", mcap->type_text);

	newmcap->subtype_code = mcap->subtype_code;
	snprintf(newmcap->subtype_text, sizeof(newmcap->subtype_text), "%s", mcap->subtype_text);

	newmcap->view = mcap->view;
	newmcap->print = mcap->print;
	newmcap->process = mcap->process;

	if(mcap->ext_mcap)
		newmcap->ext_mcap = strdup(mcap->ext_mcap);
	else
		newmcap->ext_mcap = NULL;

	snprintf(newmcap->ext, sizeof(newmcap->ext), "%s", mcap->ext);

	newmcap->encode = mcap->encode;

	return newmcap;
}

struct _mime_mailcap *find_mailcap(char *type, char *subtype, int exact) {
	int i;
	struct _mime_mailcap *mcap;
	char *p;

	if(!type || !subtype)
		return NULL;

	if((strlen(type) < 1) || (strlen(type) > 32)) {
		display_msg(MSG_WARN, "MIME", "Invalid MIME type");
		return NULL;
	}

	if(strlen(subtype) < 1)
		subtype = "*";
	else if(strlen(subtype) > 32)
		subtype[32] = '\0';

	i = 0;
	while(mailcap[i].type_code != CTYPE_UNSUPPORTED) {
		if(mailcap[i].type_code == CTYPE_DEFAULT) {
			if((exact != 1) || !strcmp(type, "*"))
				return &mailcap[i];
		}

		if(!strcasecmp(mailcap[i].type_text, type)) {
			if(mailcap[i].subtype_code == CSUBTYPE_DEFAULT) {
				if((exact != 1) || !strcmp(subtype, "*")) {
					if(exact == 2)
						return &mailcap[i];
					if((mcap = copy_mailcap(&mailcap[i])) == NULL)
						return NULL;

					mcap->type_code = CTYPE_UNSUPPORTED;
					snprintf(mcap->subtype_text, sizeof(mcap->subtype_text), "%s", subtype);
					return mcap;
				}
			}

			if(!strcasecmp(mailcap[i].subtype_text, subtype))
				return &mailcap[i];
		}

		i++;
	}

	if((i >= MAX_MCAP_SIZE) || (exact == 2))
		return NULL;

	/*
	 display_msg(MSG_WARN, "MIME", "Unknown format %s/%s", type,subtype);
	*/

	p = type;
	while(*p != '\0') {
		if(!isalpha(*p) && !isdigit(*p) && (*p != '-') &&
		   (*p != '.') && (*p != '_')) {
			display_msg(MSG_WARN, "MIME", "Invalid MIME type %s/%s", type,
						subtype);
			return NULL;
		}
		p++;
	}

	if(strcmp(subtype, "*")) {
		p = subtype;
		while(*p != '\0') {
			if(!isalpha(*p) && !isdigit(*p) && (*p != '-') &&
			   (*p != '.') && (*p != '_')) {
				display_msg(MSG_WARN, "MIME", "Invalid MIME subtype %s/%s",
							type, subtype);
				return NULL;
			}
			p++;
		}
	}

	if(
	  (mcap =
	   (struct _mime_mailcap *) malloc(sizeof(struct _mime_mailcap))) ==
	  NULL) {
		display_msg(MSG_WARN, "MIME", "malloc failed");
		return NULL;
	}

	mcap->type_code = CTYPE_UNSUPPORTED;
	snprintf(mcap->type_text, sizeof(mcap->type_text), "%s", type);

	mcap->subtype_code = CSUBTYPE_UNSUPPORTED;
	snprintf(mcap->subtype_text, sizeof(mcap->subtype_text), "%s", subtype);

	mcap->view = NULL;
	mcap->print = NULL;
	mcap->process = NULL;

	mcap->ext_mcap = NULL;

	mcap->ext[0] = '\0';
	mcap->encode = CE_NONE;

	return mcap;
}

struct _mime_mailcap *get_mailcap_entry(struct _mail_msg *msg,
										struct _mime_msg *mime) {
	struct _head_field *fld;
	char *m_type, *m_subtype;
	char typestr[64];
	char *p;

	if(msg) {
		if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
			return NULL;
	} else if(mime) {
		if((fld = find_mime_field(mime, MIME_C_TYPE)) == NULL)
			return NULL;
	} else
		return NULL;

	if((p = strchr(fld->f_line, ';')) != NULL) {
		*p = '\0';
		snprintf(typestr, sizeof(typestr), "%s", fld->f_line);
		*p = ';';
	} else
		snprintf(typestr, sizeof(typestr), "%s", fld->f_line);

	m_type = typestr;
	while(*m_type == ' ')
		m_type++;

	if((p = strchr(m_type, ' ')) != NULL)
		*p = '\0';

	if((m_subtype = strchr(m_type, '/')) == NULL) {
		if(!strcasecmp(mailcap[0].type_text, m_type))
			m_subtype = mailcap[0].subtype_text;
		else
			m_subtype = "*";
	} else {
		*m_subtype = '\0';
		m_subtype++;
	}

	return find_mailcap(m_type, m_subtype, 1);
}

int is_mime_msg(struct _mail_msg *msg) {
	struct _head_field *fld;
	char *m_type, *m_subtype;
	char typestr[64];
	char *p;

	if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
		return 0;

	if((p = strchr(fld->f_line, ';')) != NULL) {
		*p = '\0';
		snprintf(typestr, sizeof(typestr), "%s", fld->f_line);
		*p = ';';
	} else
		snprintf(typestr, sizeof(typestr), "%s", fld->f_line);

	m_type = rem_tr_space(typestr);
	if((m_subtype = strchr(m_type, '/')) == NULL)
		return 0;

	*m_subtype = '\0';
	m_subtype++;

	if(strcasecmp(mailcap[DEFAULT_MAILCAP].type_text, m_type) ||
	   strcasecmp(mailcap[DEFAULT_MAILCAP].subtype_text, m_subtype))
		return 1;

	return 0;
}

void discard_mcap(struct _mime_mailcap *mcap) {
	if(mcap->type_code != CTYPE_UNSUPPORTED)
		return;

	if(mcap->ext_mcap)
		free(mcap->ext_mcap);
	free(mcap);

	return;
}

void discard_mime(struct _mime_msg *mime) {
	struct _head_field *hf, *hf1;

	if(!mime)
		return;

	if(mime->mime_next)
		discard_mime(mime->mime_next);

	if(mime->src_info)
		free(mime->src_info);

	if(mime->c_id)
		free(mime->c_id);

	if(mime->c_descr)
		free(mime->c_descr);

	if(mime->boundary)
		free(mime->boundary);

	discard_mcap(mime->mailcap);
	hf = mime->m_fields;
	while(hf) {
		hf1 = hf->next_head_field;
		if(hf->f_line)
			free(hf->f_line);
		free(hf);
		hf = hf1;
	}

	free(mime);

	return;
}

void replace_mime_field(struct _mime_msg *mime, char *name, char *str) {
	struct _head_field *hf;

	if(!mime || !name || !str)
		return;

	if((strlen(name) < 1) || (strlen(name) >= MAX_FIELD_NAME_LEN))
		return;

	if((hf = find_mime_field(mime, name)) == NULL) {
		add_mime_field(mime, name, str);
		return;
	}

	free(hf->f_line);
	hf->f_line = strdup(str);
	strcpy(hf->f_name, name);

	return;
}

void add_mime_field(struct _mime_msg *mime, char *name, char *str) {
	struct _head_field *hf, *lf;

	if(!mime || !name || !str)
		return;

	if((strlen(name) < 1) || (strlen(name) >= MAX_FIELD_NAME_LEN))
		return;

	hf = (struct _head_field *) malloc(sizeof(struct _head_field));
	hf->f_line = strdup(str);
	strcpy(hf->f_name, name);
	hf->num_fields = 1;
	hf->next_head_field = NULL;

	if(mime->m_fields)
		mime->m_fields->num_fields = 1;

	lf = mime->m_fields;
	while(lf && lf->next_head_field) {
		if(mime->m_fields)
			mime->m_fields->num_fields++;
		lf = lf->next_head_field;
	}


	if(lf)
		lf->next_head_field = hf;
	else
		mime->m_fields = hf;

	return;
}

struct _head_field *find_mime_field(struct _mime_msg *mime, char *str) {
	struct _head_field *hf;

	if(!mime || !str)
		return NULL;

	hf = mime->m_fields;
	while(hf) {

		if(!strcasecmp(hf->f_name, str))
			return hf;

		hf = hf->next_head_field;
	}

	return NULL;

}

struct _mime_encoding *get_mime_encoding(struct _mail_msg *msg,
										 struct _mime_msg *mime) {
	int i;
	struct _head_field *fld;

	if(mime) {
		if((fld = find_mime_field(mime, MIME_C_ENCR)) == NULL)
			return &supp_encodings[DEFAULT_ENCODING];
	} else if(msg) {
		if((fld = find_field(msg, MIME_C_ENCR)) == NULL)
			return &supp_encodings[DEFAULT_ENCODING];
	} else
		return NULL;

	i = 0;
	while(supp_encodings[i].c_trans_enc != CE_UNSUPPORTED) {
		if(!strcasecmp(fld->f_line, supp_encodings[i].encoding_name))
			return &supp_encodings[i];

		i++;
	}

	display_msg(MSG_WARN, "MIME", "Unsupported encoding %s", fld->f_line);

	return &supp_encodings[UNSUPPORTED_ENCODING];
}

struct _mime_charset *get_mime_charset(struct _mail_msg *msg,
									   struct _mime_msg *mime) {
	int i;
	struct _head_field *fld;
	char *ch_name;

	if(mime) {
		if((fld = find_mime_field(mime, MIME_C_TYPE)) == NULL)
			return &supp_charsets[DEFAULT_CHARSET];
	} else if(msg) {
		if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
			return &supp_charsets[DEFAULT_CHARSET];
	} else
		return NULL;

	if((ch_name = get_fld_param(fld, "charset")) == NULL)
		return &supp_charsets[DEFAULT_CHARSET];

	i = 0;
	while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
		if(!strcasecmp(supp_charsets[i].charset_name, ch_name))
			return &supp_charsets[i];

		i++;
	}
	if(Config.getInt("disable_charset_warning", 0) != 1)
		display_msg(MSG_WARN, "MIME", "Unsupported charset %s\nassuming US-ASCII", ch_name);

	return &supp_charsets[DEFAULT_CHARSET];
}

void mime_scan(struct _mail_msg *msg) {
	struct _mime_msg *mime;
	struct _head_field *fld;
	struct _mime_mailcap *mcap;

	if(!msg)
		return;

	if(msg->mime)
		discard_mime(msg->mime);

	msg->mime = NULL;

	if((mime = (struct _mime_msg *) malloc(sizeof(struct _mime_msg))) ==
	   NULL) {
		display_msg(MSG_WARN, "MIME", "malloc failed");
		return;
	}

	mime->m_start = msg->header->header_len;
	mime->m_end = msg->msg_len;
	mime->src_info = NULL;
	mime->m_fields = NULL;

	if(!is_mime(msg)) {
		msg->mime = mime;
		mime->mailcap = &mailcap[DEFAULT_MAILCAP];
		mime->charset = &supp_charsets[DEFAULT_CHARSET];
		mime->encoding = &supp_encodings[DEFAULT_ENCODING];
		mime->mime_vers = MIME_VERS_SUPP;
		mime->c_id = NULL;
		mime->c_descr = NULL;
		mime->c_len = 0L;
		mime->m_fields = NULL;
		mime->mime_next = NULL;
		mime->boundary = NULL;
		mime->flags = TEXT_PART;
		return;
	}

	mime->mime_vers = get_mime_version(msg);

	if((mime->mailcap = get_mailcap_entry(msg, NULL)) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not find mailcap entry");
		free(mime);
		return;
	}

	if(mime->mailcap->process == NULL) {
		if((mcap = find_mailcap(mime->mailcap->type_text,
								mime->mailcap->subtype_text, 0)) != NULL) {
			mime->mailcap->process = mcap->process;
			discard_mcap(mcap);
		}
	}

	if((mime->encoding = get_mime_encoding(msg, NULL)) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not find encoding type");
		free(mime);
		return;
	}

	if((mime->charset = get_mime_charset(msg, NULL)) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not find charset type");
		free(mime);
		return;
	}

	if((fld = find_field(msg, MIME_C_ENCR)) != NULL) {
		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	}

	if((fld = find_field(msg, MIME_C_TYPE)) != NULL) {
		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	}

	if((fld = find_field(msg, MIME_C_ID)) != NULL) {
		mime->c_id = strdup(fld->f_line);

		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	} else
		mime->c_id = NULL;

	if((fld = find_field(msg, MIME_C_DESCR)) != NULL) {
		mime->c_descr = strdup(fld->f_line);

		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	} else
		mime->c_descr = NULL;

	if((fld = find_field(msg, MIME_C_LENGTH)) != NULL) {
		mime->c_len = atol(fld->f_line);
		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	} else
		mime->c_len = 0L;

	if((fld = find_field(msg, MIME_C_DISP)) != NULL) {
		fld = copy_field(fld);
		fld->next_head_field = mime->m_fields;
		mime->m_fields = fld;
	}

	mime->mime_next = NULL;
	mime->boundary = NULL;
	mime->flags = MSG_BODY;

	msg->mime = mime;

	if(!is_mime_text(mime))
		mime->flags |= ATTACHMENT;
	else
		mime->flags |= TEXT_PART;

	if(mime->mailcap->process) {
		if((*mime->mailcap->process) (msg, msg->mime) == -1) {
			display_msg(MSG_WARN, "MIME",
						"Failed to process MIME message, interpreting as text");
			mime->mailcap = &mailcap[DEFAULT_MAILCAP];
			mime->encoding = &supp_encodings[DEFAULT_ENCODING];
			mime->charset = &supp_charsets[DEFAULT_CHARSET];
			mime->flags = TEXT_PART;
		}
	}

	return;
}

int is_mime_text(struct _mime_msg *mime) {
	struct _head_field *mf;

	if(!mime)
		return 0;

	if((mf = find_mime_field(mime, MIME_C_DISP)) &&
	   get_fld_param(mf, "attachment"))
		return 0;

	if(mime->mailcap != &mailcap[DEFAULT_MAILCAP])
		return 0;

	return 1;
}

int is_boundary(char *boundary, char *str) {
	int blen = 0;


	if( !boundary || (*str++ != '-') || (*str++ != '-'))
		return 0;
	
	blen = strlen(boundary);

	if(strncmp(str, boundary, blen)) 
		return 0;
	
	str += blen;
	if(*str == '\0') 
		return 1;
	
	if((*str++ == '-') && (*str++ == '-') && (*str == '\0')) 
		return 2;
	
	return 0;
}

struct _mime_msg *scan_part(char *boundary, FILE * mfd) {
	char buf[255];
	char *p;
	struct _mime_msg *mime;
	long offt, short_str;
	struct _head_field *fld, *lf;
	int i;

	if((mime = (struct _mime_msg *) malloc(sizeof(struct _mime_msg))) ==
	   NULL) {
		display_msg(MSG_WARN, "MIME", "malloc failed");
		return NULL;
	}

	mime->mailcap = &mailcap[DEFAULT_MAILCAP];
	mime->encoding = &supp_encodings[DEFAULT_ENCODING];
	mime->charset = &supp_charsets[DEFAULT_CHARSET];

	mime->src_info = NULL;
	mime->c_id = NULL;
	mime->c_descr = NULL;
	lf = mime->m_fields = NULL;
	mime->mime_next = NULL;
	mime->c_len = 0L;
	mime->boundary = !boundary ? NULL : strdup(boundary);
	mime->mime_vers = MIME_VERS_SUPP;

	offt = mime->m_start = mime->m_end = ftell(mfd);
	mime->flags = ATTACHMENT;

	if(fgets(buf, 254, mfd) == NULL) {
		free(mime->boundary);
		free(mime);
		return NULL;
	}

	buf[254] = '\0';
	strip_newline(buf);

	if(strlen(buf) < 1) {
		offt = mime->m_start = mime->m_end = ftell(mfd);
		fgets(buf, 254, mfd);
		strip_newline(buf);
	}

	if((i = is_boundary(boundary, buf)) == 0) {
		mime->flags = PREAMBLE;
		offt = ftell(mfd);
		while(fgets(buf, 254, mfd)) {
			strip_newline(buf);
			if(is_boundary(boundary, buf))
				break;

			offt = ftell(mfd);
		}
		mime->m_end = offt;

		return mime;
	}

	if(i == 2) {
		mime->flags = EPILOG;
		fseek(mfd, 0, SEEK_END);
		mime->m_end = ftell(mfd);

		return mime;
	}

	offt = ftell(mfd);
	while(fgets(buf, 254, mfd)) {
		strip_newline(buf);

		if(strlen(buf) < 1)
			break;

		if(is_boundary(boundary, buf)) {
			mime->m_end = offt;
			goto ret_mime;
		}

		offt = ftell(mfd);

		if((fld = get_field(buf)) == NULL)
			continue;

		offt = ftell(mfd);
		while(fgets(buf, 254, mfd) &&
			  ((buf[0] == ' ') || (buf[0] == 0x09)) &&
			  (strlen(fld->f_line) < MAX_FIELD_LEN)) {

			strip_newline(buf);
			p = buf;
			while((p[1] == ' ') || (p[1] == 0x09))
				p++;

			*p = ' ';

			fld->f_line =
			(char *) realloc(fld->f_line,
							 strlen(p) + strlen(fld->f_line) + 1);
			strcat(fld->f_line, p);
			offt = ftell(mfd);
		}

		fseek(mfd, offt, SEEK_SET);


		if(lf)
			lf->next_head_field = fld;
		else
			mime->m_fields = fld;
		lf = fld;
	}

	if(ferror(mfd) || feof(mfd)) {
		//fprintf(stderr, "EOF\n");
		mime->m_end = offt;
		if(i == 2)
			mime->flags = EPILOG;
		else
			goto ret_mime;
		return mime;
	}

	offt = ftell(mfd);
	short_str = -1;
	while(fgets(buf, 254, mfd)) {

		strip_newline(buf);

		if(is_boundary(boundary, buf)) {
			if(short_str == -1)
				mime->m_end = offt;
			else
				mime->m_end = short_str;
			goto ret_mime;
		}

		if(strlen(buf) < 1)
			short_str = offt;
		else
			short_str = -1;

		offt = ftell(mfd);
	}

	if(feof(mfd) && (is_boundary(boundary, buf) || (i == 1))) {
		mime->m_end = offt;
		goto ret_mime;
	}

	mime->flags = EPILOG;
	mime->m_end = offt;

	return mime;

	ret_mime:if((mime->mailcap = get_mailcap_entry(NULL, mime)) ==
				NULL) mime->mailcap = &mailcap[DEFAULT_MAILCAP];

	if((mime->encoding = get_mime_encoding(NULL, mime)) == NULL)
		mime->encoding = &supp_encodings[DEFAULT_ENCODING];

	if((mime->charset = get_mime_charset(NULL, mime)) == NULL)
		mime->charset = &supp_charsets[DEFAULT_CHARSET];

	if((fld = find_mime_field(mime, MIME_C_ID)) != NULL)
		mime->c_id = strdup(fld->f_line);
	else
		mime->c_id = NULL;

	if((fld = find_mime_field(mime, MIME_C_DESCR)) != NULL)
		mime->c_descr = strdup(fld->f_line);
	else
		mime->c_descr = NULL;

	if((fld = find_mime_field(mime, MIME_C_LENGTH)) != NULL)
		mime->c_len = atol(fld->f_line);
	else
		mime->c_len = 0L;

	return mime;
}

int process_multipart(struct _mail_msg *msg, struct _mime_msg *mime) {
	char *bnd;
	FILE *mfd;
	struct _mime_msg *mins, *mnext, *text_part, *malt, *maltn;
	struct _head_field *fld;
	long sk=0;

	if(!msg || !mime)
		return -1;

	if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
		return -1;

	if((bnd = get_fld_param(fld, "boundary")) == NULL) {
		display_msg(MSG_WARN, "MIME",
			"Can not find boundary for multipart");
		return -1;
	}

	if(strlen(bnd) > 70) {
		display_msg(MSG_WARN, "MIME", "Boundary too long");
		return -1;
	}

	mime->flags &= ~ATTACHMENT;
	mime->boundary = strdup(bnd);

	if((mfd = fopen(msg->get_file(msg), "r")) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not open %s",
			msg->get_file(msg));
		return -1;
	}

	if(fseek(mfd, msg->header->header_len, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "MIME", "Can not seek in %s",
			msg->get_file(msg));
		return -1;
	}

	text_part = NULL;
	mins = mime;
	while((mnext = scan_part(mime->boundary, mfd)) != NULL) {
		mins->mime_next = mnext; 
		mins = mnext;
		fseek(mfd, mnext->m_end, SEEK_SET);

		if((mime->mailcap->subtype_code == CSUBTYPE_DIGEST) && 
			(mnext->mailcap->type_code != CTYPE_MESSAGE)) {
			if(mnext->mailcap->type_code == CTYPE_UNSUPPORTED) {
				if(mnext->mailcap->ext_mcap)
					free(mnext->mailcap->ext_mcap);
				free(mnext->mailcap);
			}
			mnext->mailcap = &mailcap[MESSAGE_MAILCAP];
		} 
		else {
			if(!text_part && (mnext->mailcap->type_code == CTYPE_MULTIPART) && 
				(mnext->mailcap->subtype_code == CSUBTYPE_ALTERNATIVE)) {
				fld = find_mime_field(mnext, MIME_C_TYPE);
				bnd = get_fld_param(fld, "boundary");
				sk = mnext->m_end;
				mnext->flags &= ~ATTACHMENT;
				malt = mnext;
				malt->boundary = strdup(bnd);
				fseek(mfd, mnext->m_start, SEEK_SET);
				while((maltn = scan_part(malt->boundary, mfd)) != NULL) {
					malt->mime_next = maltn;
					malt = maltn;
					fseek(mfd, maltn->m_end, SEEK_SET);
					if(!text_part && (maltn->mailcap->type_code == CTYPE_TEXT) && 
						(maltn->mailcap->subtype_code == CSUBTYPE_PLAIN)) {
						maltn->flags &= ~ATTACHMENT;
						text_part = maltn;
						maltn->flags |= TEXT_PART;
					}
					else {
						mins->mime_next = maltn;
						mins = maltn;
					}

					if(maltn->flags & EPILOG)
						break;
				}
				fseek(mfd, sk, SEEK_SET);
			}

			if(!text_part && (mnext->flags & ATTACHMENT) && is_mime_text(mnext)) {
				text_part = mnext;
				mnext->flags |= TEXT_PART;
			}
		}
		if(mnext->flags & EPILOG)
			break;
	}

	fclose(mfd);

	return 0;
}

void view_local_exit(struct _proc_info *pinfo) {
	if(pinfo && pinfo->u_data)
		free(pinfo->u_data);
}

void view_part_exit(struct _proc_info *pinfo) {
	if(pinfo && pinfo->u_data) {
		unlink((char *) pinfo->u_data);
		free(pinfo->u_data);
	}
}

int view_part(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _proc_info pinfo;
	struct _mime_mailcap *mcap, *mcap1;
	struct _mime_msg *mime1;
	char m_tmp_file[255];
	char buf[255];

	if(!msg || !mime)
		return -1;

	if((mcap = find_mailcap(mime->mailcap->type_text,
							mime->mailcap->subtype_text, 0)) == NULL)
		return -1;
	mcap1 = mcap;

	mime1 = msg->mime;
	while(mime1) {
		mime1->flags &= ~LOCK_PART;
		mime1 = mime1->mime_next;
	}

	if((mcap != mime->mailcap) &&
	   !mime->mailcap->view && !mime->mailcap->ext_mcap) {
		if((mcap->type_code == CTYPE_DEFAULT) &&
		   ((mcap = mcap_select(mime->mailcap->type_text,
								mime->mailcap->subtype_text)) == NULL)) {
			discard_mcap(mcap1);
			return 0;
		}
		mime->mailcap = mcap;
	}

	if (mcap1 != mime->mailcap)
		discard_mcap(mcap1);
	mcap1 = NULL;
	mcap = NULL;

	if(!mime->mailcap->view && !mime->mailcap->ext_mcap) {
		if((mcap = find_mailcap("*", "*", 1)) != NULL) {
			if((mcap = mcap_select(mime->mailcap->type_text,
								   mime->mailcap->subtype_text)) == NULL)
				return 0;
			mime->mailcap = mcap;
		}
	}

	if(!mime->mailcap->ext_mcap) {
		if(mime->mailcap->view)
			return(*mime->mailcap->view) (msg, mime);
		else
			display_msg(MSG_WARN, "Don't know how to view", "%s/%s",
						mime->mailcap->type_text,
						mime->mailcap->subtype_text);
		return -1;
	}

	if(!*mime->mailcap->ext)
		strcpy(m_tmp_file, get_temp_file("view"));
	else
		snprintf(m_tmp_file, sizeof(m_tmp_file), "%s.%s",
				 get_temp_file("view"), mime->mailcap->ext);

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "view", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}

	if(strstr(mime->mailcap->ext_mcap, "%s") == NULL)
		snprintf(buf, sizeof(buf), "%s %s", mime->mailcap->ext_mcap,
				 m_tmp_file);
	else
		snprintf(buf, sizeof(buf), mime->mailcap->ext_mcap, m_tmp_file,
				 m_tmp_file);

	init_pinfo(&pinfo);
	pinfo.wait = WAIT_ASYNC;
	pinfo.u_data = strdup(m_tmp_file);
	pinfo.ul_data = 0L;
	pinfo.handle = view_part_exit;

	if(exec_child(buf, &pinfo) == -1)
		view_part_exit(&pinfo);

	return 0;
}

struct _mime_msg *get_text_part(struct _mail_msg *msg) {
	struct _mime_msg *mime;

	if(!msg->mime)
		mime_scan(msg);

	mime = msg->mime;

	while(mime) {
		if(mime->flags & TEXT_PART)
			return mime;

		mime = mime->mime_next;
	}

	return NULL;
}

struct _mime_msg *get_any_text_part(struct _mail_msg *msg) {
	struct _mime_msg *mime;

	if((mime = get_text_part(msg)) != NULL)
		return mime;

	mime = msg->mime;
	while(mime) {
		if(mime->mailcap->subtype_code == CSUBTYPE_PGP) {
			mime->flags |= PGP_PART;
			return mime;
		}

		if(mime->mailcap->subtype_code == CSUBTYPE_PGPENC) {
			mime = msg->mime;
			while(mime) {
				if(mime->mailcap == &mailcap[BINARY_MAILCAP]) {
					mime->flags |= PGP_PART;
					return mime;
				}
				mime = mime->mime_next;
			}

			return NULL;
		}

		if(!strcasecmp(mime->mailcap->type_text, "text"))
			return mime;
		mime = mime->mime_next;
	}

	return NULL;
}

void
print_mime_msg_header(struct _mime_msg *mime, struct _mail_msg *msg,
					  FILE * mfd) {
	struct _head_field *hf;
	int prctype = 1, prcenc = 1;

	if(!mfd || !msg)
		return;

	msg->get_header(msg);

	hf = msg->header->other_fields;
	while(hf) {
		if(strncasecmp(hf->f_name, "Content-", 8))
			print_header_field(hf, mfd, 0);
		hf = hf->next_head_field;
	}

	if(mime) {
		if(mime->encoding->c_trans_enc ==
		   supp_encodings[DEFAULT_ENCODING].c_trans_enc) prcenc = 0;

		if(!(mime->flags & ATTACHMENT))
			if((mime->charset->charset_code ==
				supp_charsets[DEFAULT_CHARSET].charset_code) &&
			   (mime->mailcap->type_code ==
				mailcap[DEFAULT_MAILCAP].type_code) &&
			   (mime->mailcap->subtype_code ==
				mailcap[DEFAULT_MAILCAP].subtype_code)) prctype = 0;
		hf = mime->m_fields;
		while(hf) {
			if(!prctype && !strcasecmp(hf->f_name, MIME_C_TYPE)) {
				hf = hf->next_head_field;
				continue;
			}

			if(!prcenc && !strcasecmp(hf->f_name, MIME_C_ENCR)) {
				hf = hf->next_head_field;
				continue;
			}

			print_header_field(hf, mfd, 0);
			hf = hf->next_head_field;
		}
	}

	fprintf(mfd, "%s: %04X\n", STATUS_FIELD, msg->flags & 0xFFFF);
	print_addr(msg->header->Sender, "Sender", mfd, -2);
	print_addr(msg->header->From, "From", mfd, -2);
	print_addr(msg->header->To, "To", mfd, -2);
	if(msg->header->News)
		print_news_addr(msg->header->News, "News", mfd);
	if(msg->header->Subject)
		fprintf(mfd, "Subject: %s\n", msg->header->Subject);

	print_addr(msg->header->Cc, "Cc", mfd, -2);
	print_addr(msg->header->Bcc, "Bcc", mfd, -2);

	return;
}

void print_mime_header(struct _mime_msg *mime, FILE * mfd) {
	struct _head_field *hf;
	int prctype = 1, prcenc = 1;

	if(!mime || !mfd)
		return;

	if(mime->encoding->c_trans_enc ==
	   supp_encodings[DEFAULT_ENCODING].c_trans_enc) prcenc = 0;

	if(!(mime->flags & ATTACHMENT))
		if((mime->charset->charset_code ==
			supp_charsets[DEFAULT_CHARSET].charset_code) &&
		   (mime->mailcap->type_code ==
			mailcap[DEFAULT_MAILCAP].type_code) &&
		   (mime->mailcap->subtype_code ==
			mailcap[DEFAULT_MAILCAP].subtype_code)) prctype = 0;

	hf = mime->m_fields;
	while(hf) {
		if(!prctype && !strcasecmp(hf->f_name, MIME_C_TYPE)) {
			hf = hf->next_head_field;
			continue;
		}

		if(!prcenc && !strcasecmp(hf->f_name, MIME_C_ENCR)) {
			hf = hf->next_head_field;
			continue;
		}

		print_header_field(hf, mfd, 0);
		hf = hf->next_head_field;
	}

	fprintf(mfd, "\n");
	return;
}

int digest_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	char m_tmp_file[255];
	struct _mail_msg *msg1;
	struct _mime_msg *mime1, *mime2;
	FILE *dfd;
	long nnum;

	if(!msg || !mime)
		return -1;

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "digest", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);

	if((dfd = fopen(m_tmp_file, "w")) == NULL) {
		display_msg(MSG_WARN, "digest", "Can not open %s", m_tmp_file);
		return -1;
	}

	print_mime_msg_header(mime, msg, dfd);
	fputc('\n', dfd);

	mime1 = msg->mime;
	mime2 = mime->mime_next;
	msg->mime = mime;
	mime->mime_next = NULL;

	if(write_part(mime, msg, dfd) == -1) {
		display_msg(MSG_WARN, "digest", "Can not write MIME part!");
		fclose(dfd);
		unlink(m_tmp_file);
		return -1;
	}

	msg->mime = mime1;
	mime->mime_next = mime2;

	fclose(dfd);

	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "view", "Can not parse message");
		unlink(m_tmp_file);
		return -1;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);
	return 0;
}

struct _mail_msg *get_mime_msg(struct _mail_msg *msg,
							   struct _mime_msg *mime) {
	char m_tmp_file[255];
	struct _mail_msg *msg1;
	struct _mime_msg *mime1;
	long nnum;

	if(!msg || !mime)
		return NULL;

	if(!(mime->flags & ATTACHMENT))
		return NULL;

	if(mime->mailcap->type_code != CTYPE_MESSAGE)
		return NULL;

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "MIME", "No space in %s", FTEMP);
		return NULL;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "view", "Can not save MIME part!");
		unlink(m_tmp_file);
		return NULL;
	}

	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "view", "Can not parse message");
		unlink(m_tmp_file);
		return NULL;
	}

	mime1 = msg->mime;
	while(mime1) {
		mime1->flags &= ~LOCK_PART;
		mime1 = mime1->mime_next;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	return msg1;
}

int msg_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	char m_tmp_file[255];
	struct _mail_msg *msg1;
	long nnum;

	if(!msg || !mime)
		return -1;

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "digest", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "view", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}

	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "view", "Can not parse message");
		unlink(m_tmp_file);
		return -1;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);
	return 0;
}

int mime_play(struct _mail_msg *msg, struct _mime_msg *mime) {
	int res;
	char m_tmp_file[255];

	if(!msg || !mime)
		return -1;

	strcpy(m_tmp_file, get_temp_file("splay"));

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "play sound", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}

	res = play_sound(m_tmp_file, 100);

	unlink(m_tmp_file);

	return res;
}

int pgpkeys_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	char m_tmp_file[255];

	if(!msg || !mime)
		return -1;

	strcpy(m_tmp_file, get_temp_file("pgpkey"));
	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "extract PGP key",
					"Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}
	pgp_action(m_tmp_file, EXTKEY, NULL);

	unlink(m_tmp_file);
	return 0;
}

int pgpenc_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _mail_msg *msg1;
	struct _mime_msg *mtext, *menc, *mtemp;
	struct _head_field *fld;
	char m_tmp_file[255], m_tmp_file1[255], m_tmp_enc[255], *p;
	FILE *mfd, *pfd;
	long nnum;
	struct pgpargs pgpargs;

	if(!msg || !mime)
		return -1;

	strcpy(m_tmp_enc, get_temp_file("pgpenc"));

	if(save_part(msg, mime, m_tmp_enc, 0) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not save PGP encoding");
		unlink(m_tmp_enc);
		return -1;
	}

	nnum = 0;
	if((mfd = fopen(m_tmp_enc, "r")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not open\n%s", m_tmp_enc);
		return -1;
	}
	while(fgets(m_tmp_file, 255, mfd)) {
		strip_newline(m_tmp_file);
		if(!*m_tmp_file)
			continue;
		if(strcasecmp(m_tmp_file, "Version: 1")) {
			display_msg(MSG_WARN, "PGP-MIME",
						"Invalid line in pgp-encrypted");
			fclose(mfd);
			unlink(m_tmp_enc);
			return -1;
		} else
			nnum = 1;
	}

	fclose(mfd);
	unlink(m_tmp_enc);

	if(nnum == 0) {
		display_msg(MSG_WARN, "PGP-MIME", "No 'Version: 1' field");
		return -1;
	}

	menc = mtext = NULL;

	mtemp = msg->mime;
	while(mtemp) {
		if((mtemp->flags & PREAMBLE) || (mtemp->flags & EPILOG)) {
			mtemp = mtemp->mime_next;
			continue;
		}

		if(mtemp->flags & MSG_BODY) {
			if((mtemp->mailcap->type_code == CTYPE_MULTIPART) &&
			   (mtemp->mailcap->subtype_code == CSUBTYPE_ENCRYPTED))
				menc = mtemp;
			else {
				display_msg(MSG_WARN, "PGP-MIME",
							"Not a multipart/encrypted");
				return -1;
			}
		} else {
			if(mtemp != mime) {
				if((mtemp->mailcap->type_code == CTYPE_APPLICATION) &&
				   (mtemp->mailcap->subtype_code == CSUBTYPE_OSTREAM)) {
					if(mtext) {
						display_msg(MSG_WARN, "PGP-MIME",
									"Too many encrypted parts");
						return -1;
					}
					mtext = mtemp;
				} else {
					display_msg(MSG_WARN, "PGP-MIME",
								"Unidentified part in multipart/encrypted");
					return -1;
				}
			}
		}
		mtemp = mtemp->mime_next;
	}

	if(!menc) {
		display_msg(MSG_WARN, "PGP-MIME", "Not a multipart/encrypted");
		return -1;
	}

	if(!mtext) {
		display_msg(MSG_WARN, "PGP-MIME", "PGP encrypted part is missing");
		return -1;
	}

	if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
		return -1;

	if((p = get_fld_param(fld, "protocol")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME",
					"No 'protocol' paramter\nin multipart/encrypted");
		return -1;
	}

	if(strcasecmp(p, "application/pgp-encrypted")) {
		display_msg(MSG_WARN, "PGP-MIME",
					"Invalid 'protocol' paramter\nin multipart/encrypted");
		return -1;
	}

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);
	snprintf(m_tmp_file1, sizeof(m_tmp_file1), "%s/%ld.tmp",
			 ftemp->fold_path, nnum);

	if(save_part(msg, mtext, m_tmp_file1, 0) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not save MIME part!");
		unlink(m_tmp_file1);
		return -1;
	}

	init_pgpargs(&pgpargs);
	pgpargs.passphrase = input_passphrase();
	pgpargs.msg = msg;
	if(pgp_action(m_tmp_file1, DECODE, &pgpargs) < 0) {
		if(pgpargs.passphrase)
			free(pgpargs.passphrase);
		unlink(m_tmp_file1);
		return -1;
	}
	if(pgpargs.passphrase)
		free(pgpargs.passphrase);

	if((mfd = fopen(m_tmp_file1, "r")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not open %s", m_tmp_file1);
		unlink(m_tmp_file1);
		return -1;
	}

	if((pfd = fopen(m_tmp_file, "w")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not open %s", m_tmp_file);
		unlink(m_tmp_file);
		unlink(m_tmp_file1);
		return -1;
	}

	print_mime_msg_header(NULL, msg, pfd);
	while(fgets(m_tmp_enc, 255, mfd))
		fputs(m_tmp_enc, pfd);
	fclose(mfd);
	fclose(pfd);
	unlink(m_tmp_file1);

	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not parse message");
		unlink(m_tmp_file);
		return -1;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);

	return 0;
}

int pgpsig_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _mime_msg *mtext, *msig, *mtemp;
	struct _head_field *fld;
	char m_tmp_file[255], m_tmp_sig[255], *p;
	long nnum;
	struct pgpargs pgpargs;

	if(!msg || !mime)
		return -1;

	msig = mtext = NULL;

	init_pgpargs(&pgpargs);
	mtemp = msg->mime;
	while(mtemp) {
		if((mtemp->flags & PREAMBLE) || (mtemp->flags & EPILOG)) {
			mtemp = mtemp->mime_next;
			continue;
		}

		if(mtemp->flags & MSG_BODY) {
			if((mtemp->mailcap->type_code == CTYPE_MULTIPART) &&
			   (mtemp->mailcap->subtype_code == CSUBTYPE_SIGNED))
				msig = mtemp;
			else {
				display_msg(MSG_WARN, "PGP-MIME",
							"Not a multipart/signed");
				return -1;
			}
		} else {
			if((mtemp != mime) && (mtext != mtemp)) {
				if(mtext) {
					display_msg(MSG_WARN, "PGP-MIME",
								"Too many signed parts");
					return -1;
				}
				mtext = mtemp;
			}
		}
		mtemp = mtemp->mime_next;
	}

	if(!msig) {
		display_msg(MSG_WARN, "PGP-MIME", "Not a multipart/signed");
		return -1;
	}

	if(!mtext) {
		display_msg(MSG_WARN, "PGP-MIME", "PGP signed part is missing");
		return -1;
	}

	if((fld = find_field(msg, MIME_C_TYPE)) == NULL)
		return -1;

	if((p = get_fld_param(fld, "protocol")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME",
					"No 'protocol' paramter\nin multipart/signed");
		return -1;
	}

	if(strcasecmp(p, "application/pgp-signature")) {
		display_msg(MSG_WARN, "PGP-MIME",
					"Invalid 'protocol' paramter\nin multipart/signed");
		return -1;
	}

	if((p = get_fld_param(fld, "micalg")) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME",
					"No 'micalg' paramter\nin multipart/signed");
		return -1;
	}

	if(strncasecmp(p, "pgp-", 4)) {
		display_msg(MSG_WARN, "PGP-MIME",
					"Invalid 'micalg' paramter\nin multipart/signed");
		return -1;
	}

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);

	if(save_part(msg, mtext, m_tmp_file,
				 SAVEPART_HEADER | SAVEPART_RAW) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}


	strcpy(m_tmp_sig, get_temp_file("pgpsig"));

	if(save_part(msg, mime, m_tmp_sig, 0) == -1) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not save PGP signature!");
		unlink(m_tmp_file);
		unlink(m_tmp_sig);
		return -1;
	}

	pgpargs.recp = m_tmp_sig;
	pgpargs.msg = msg;
	if(pgp_action(m_tmp_file, VRFYFL, &pgpargs) < 0) {
		unlink(m_tmp_file);
		unlink(m_tmp_sig);
		return -1;
	}

	unlink(m_tmp_sig);

#if 0
	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "PGP-MIME", "Can not parse message");
		unlink(m_tmp_file);
		return -1;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);
#else
	unlink(m_tmp_file);
#endif

	return 0;
}

int pgp_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _head_field *fld;
	char m_tmp_file[255], m_tmp_file1[255], buf[255];
	char *format, *p;
	FILE *mfd, *sfd;
	int action;
	long nnum;
	struct _mail_msg *msg1;
	struct pgpargs pgpargs;

	if(!&msg || !mime)
		return -1;

	format = NULL;

	init_pgpargs(&pgpargs);
	if((fld = find_mime_field(mime, MIME_C_TYPE)) == NULL)
		return -1;

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "pgpview", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s/%ld", ftemp->fold_path,
			 nnum);
	snprintf(m_tmp_file1, sizeof(m_tmp_file1), "%s/%ld.tmp",
			 ftemp->fold_path, nnum);

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "pgpview", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}

	action = DECODE;
	if((p = get_fld_param(fld, "x-action")) != NULL) {
		if(!strcasecmp(p, "encryptsign"))
			action = DECODE | VERIFY;
		else if(!strcasecmp(p, "sign") || !strcasecmp(p, "signclear"))
			action = VERIFY;
	}

	if((format = get_fld_param(fld, "format")) != NULL) {
		if(!strcasecmp(format, "keys-only"))
			action = EXTKEY;
	}

	if(action & DECODE)
		pgpargs.passphrase = input_passphrase();

	pgpargs.msg = msg;
	pgp_action(m_tmp_file, action, &pgpargs);

	if(pgpargs.passphrase)
		free(pgpargs.passphrase);

	if(!format || strcasecmp(format, "mime")) {
		if((sfd = fopen(m_tmp_file, "r")) == NULL) {
			display_msg(MSG_WARN, "pgpview", "Can not open %s",
						m_tmp_file);
			unlink(m_tmp_file);
			return -1;
		}
		if((mfd = fopen(m_tmp_file1, "w")) == NULL) {
			display_msg(MSG_WARN, "pgpview", "Can not open %s",
						m_tmp_file1);
			unlink(m_tmp_file);
			return -1;
		}
		print_mime_msg_header(NULL, msg, mfd);
		fputc('\n', mfd);
		while(fgets(buf, 255, sfd))
			fputs(buf, mfd);
		fclose(mfd);
		fclose(sfd);
#ifdef __EMX__          /* Under OS/2 the file will not be deleted during rename() */
		if(access(m_tmp_file, 0) == 0) {
			if(unlink(m_tmp_file) != 0) {
				display_msg(MSG_WARN, "unlink", "delete %s before moving",
							m_tmp_file);
			}
		}
#endif
		if(rename(m_tmp_file1, m_tmp_file) == -1) {
			display_msg(MSG_WARN, "pgpview", "rename failed");
			unlink(m_tmp_file);
			unlink(m_tmp_file1);
			return -1;
		}
	}

	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "pgpview", "Can not parse message");
		unlink(m_tmp_file);
		return -1;
	}

	msg1->flags |= M_TEMP;
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = &msg;
	msg1->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);

	return 0;
}

int text_view(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _proc_info pinfo;
	char m_tmp_file[255];

	if(!msg || !mime)
		return -1;

	snprintf(m_tmp_file, sizeof(m_tmp_file), "%s.txt",
			 get_temp_file("tview"));

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "view", "Can not save MIME part!");
		unlink(m_tmp_file);
		return -1;
	}

	init_pinfo(&pinfo);
	pinfo.wait = WAIT_ASYNC;
	pinfo.u_data = strdup(m_tmp_file);
	pinfo.ul_data = 0L;
	pinfo.handle = view_part_exit;

	if(file_view(m_tmp_file, &pinfo) == -1)
		view_part_exit(&pinfo);

	return 0;
}

int text_print(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _proc_info pinfo;
	char m_tmp_file[255];

	if(!msg || !mime)
		return -1;

	strcpy(m_tmp_file, get_temp_file("lpr"));

	if(save_part(msg, mime, m_tmp_file, 0) == -1) {
		display_msg(MSG_WARN, "lpr", "Can not print message!");
		unlink(m_tmp_file);
		return -1;
	}

	init_pinfo(&pinfo);
	pinfo.wait = WAIT_BG;
	pinfo.u_data = strdup(m_tmp_file);
	pinfo.ul_data = 0L;
	pinfo.handle = view_part_exit;

	if(exec_child(get_print_command(m_tmp_file), &pinfo) == -1)
		view_part_exit(&pinfo);

	return 0;
}

struct _mime_msg *create_mime() {
	struct _mime_msg *mime;

	if((mime = (struct _mime_msg *) malloc(sizeof(struct _mime_msg))) ==
	   NULL) {
		display_msg(MSG_WARN, "MIME", "malloc failed");
		return NULL;
	}

	mime->m_start = mime->m_end = 0L;
	mime->src_info = NULL;
	mime->mime_vers = MIME_VERS_SUPP;
	mime->mailcap = &mailcap[DEFAULT_MAILCAP];
	mime->encoding = &supp_encodings[DEFAULT_ENCODING];
	mime->charset = &supp_charsets[DEFAULT_CHARSET];

	mime->c_id = NULL;
	mime->c_descr = NULL;
	mime->c_len = 0L;

	mime->m_fields = NULL;
	mime->mime_next = NULL;
	mime->boundary = NULL;

	mime->flags = 0;

	return mime;
}

int write_part(struct _mime_msg *mime, struct _mail_msg *msg, FILE * sfd) {
	FILE *mfd;
	char buf[255];
	char *p;
	int i;
	int one_part;
	int str_len, cur_len;

	if(!mime || !msg || !sfd)
		return -1;

	one_part = 0;
	if(!msg->mime)
		return -1;

	if(!msg->mime->mime_next) {
		if(msg->mime != mime)
			return -1;
		one_part = 1;
	}

	if(mime->flags & PREAMBLE) {
		if(strlen(XF_PREAMBLE) > 1) {
			fputs(XF_PREAMBLE, sfd);
			fputs("\n", sfd);
		}

		return 0;
	}

	if(mime->flags & EPILOG) {
		if(!mime->boundary)
			return -1;
		fprintf(sfd, "--%s--\n", mime->boundary);
		if(strlen(XF_EPILOG) > 1) {
			fputs(XF_EPILOG, sfd);
			fputs("\n", sfd);
		}

		return 0;
	}

	if((!one_part) && mime->boundary) {
		fprintf(sfd, "--%s\n", mime->boundary);
		print_mime_header(mime, sfd);
	}

	str_len = 0;
	if(mime->src_info) {
		if((mfd = fopen(mime->src_info, "r")) == NULL) {
			display_msg(MSG_WARN, "MIME", "Can not open %s",
						mime->src_info);
			return -1;
		}

		(*mime->encoding->ce_enc) (NULL, 0);
		if(mime->flags & TEXT_PART)
			str_len = MAX_INSFILE_LINE;
		else
			str_len = mime->encoding->str_len;
		cur_len = 0;
		while((i = fread(buf, 1, 150, mfd)) > 0) {
			buf[i] = '\0';
			if((p = (*mime->encoding->ce_enc) (buf, i)) == NULL) {
				display_msg(MSG_WARN, "MIME", "Encoding failed");
				fclose(mfd);

				if((mime->flags & TEXT_PART) || (mime->flags & FILE_TEMP)) {
					unlink(mime->src_info);
					free(mime->src_info);
					mime->src_info = NULL;
				}

				return -1;
			}

			while(*p != '\0') {

				if((cur_len >= str_len) && (*p != '\n')) {
					fputc('\n', sfd);
					cur_len = 0;
				}

				fputc(*p, sfd);
				if(*p == '\n')
					cur_len = 0;
				else
					cur_len++;
				p++;
			}

		}

		p = (*mime->encoding->ce_enc) (NULL, 0);
		if(p && (strlen(p) > 1)) {
			while(*p != '\0') {

				if((cur_len >= str_len) && (*p != '\n')) {
					fputc('\n', sfd);
					cur_len = 0;
				}

				fputc(*p, sfd);
				if(*p == '\n')
					cur_len = 0;
				else
					cur_len++;
				p++;
			}
		}

		if(cur_len)
			fputc('\n', sfd);

		fclose(mfd);
		if((mime->flags & TEXT_PART) || (mime->flags & FILE_TEMP)) {
			unlink(mime->src_info);
			free(mime->src_info);
			mime->src_info = NULL;
		}

	} else {
		if((mfd = fopen(msg->get_file(msg), "r")) == NULL) {
			display_msg(MSG_WARN, "MIME", "Can not open %s",
						msg->get_file(msg));
			return -1;
		}

		fseek(mfd, mime->m_start, SEEK_SET);
		if(!(mime->flags & MSG_BODY) && mime->boundary) {
			mime->flags &= ~MSG_BODY;
			while(fgets(buf, 255, mfd)) {
				strip_newline(buf);
				if(strlen(buf) < 1)
					break;
			}
		}

		if(ferror(mfd)) {
			display_msg(MSG_WARN, "MIME", "Can not read from %s",
						msg->get_file(msg));
			fclose(mfd);
			return -1;
		}

		if(mime->flags & TEXT_PART) {
			while((ftell(mfd) < mime->m_end) && fgets(buf, 255, mfd)) {
				p = (*mime->encoding->ce_enc) (buf, strlen(buf));
				if(!p) {
					display_msg(MSG_WARN, "MIME",
								"Failed to encode text part");
					fclose(mfd);
					return -1;
				}
				fputs(p, sfd);
			}
		} else {
			while((ftell(mfd) < mime->m_end) && fgets(buf, 255, mfd))
				fputs(buf, sfd);
		}

		fclose(mfd);
	}

	if(!one_part && mime->boundary)
		fputc('\n', sfd);

	return 0;
}

int update_mime(struct _mail_msg *msg) {
	struct _mime_msg *mime, *mprev;
	char buf[255];
	struct _head_field *hf, *hf1;
	FILE *mfd;
	char boundary[255];
	long hlen;
	struct stat sb;
	int mparts;

	if(!msg)
		return -1;

	if(!msg->mime)
		mime_scan(msg);

	if(!msg->mime)
		return -1;

	/* strip all MIME headers */
	if((hf = find_field(msg, MIME_C_TYPE)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_VERSION)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_C_ENCR)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_C_DESCR)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_C_ID)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_C_LENGTH)) != NULL)
		delete_field(msg, hf);

	if((hf = find_field(msg, MIME_C_DISP)) != NULL)
		delete_field(msg, hf);

	/* count MIME parts */
	mparts = 0;
	mime = msg->mime;
	while(mime) {
		if(mime->src_info) {
			if(stat(mime->src_info, &sb) == -1)
				mime->flags |= DELETE_PART;

			else {
				if(sb.st_size == 0) {
					if(mime->flags & FILE_TEMP)
						unlink(mime->src_info);
					mime->flags |= DELETE_PART;
				}

				/*if(sb.st_size > MAX_MSG_LEN) {
				   display_msg(MSG_WARN, "MIME", "Attachment is too big");
				   mime->flags |= DELETE_PART;
				   } */
			}

		}

		if(!(mime->flags & DELETE_PART) &&
		   ((mime->flags & ATTACHMENT) || (mime->flags & TEXT_PART)))
			mparts++;

		mime = mime->mime_next;
	}

	/* no parts or all parts are going to be deleted */
	if(mparts == 0) {
		sprintf(buf, "%02d", MIME_VERS_SUPP);
		buf[2] = buf[1];
		buf[1] = '.';
		buf[3] = '\0';
		add_field(msg, MIME_VERSION, buf);

		if((mime = get_any_text_part(msg)) != NULL) {
			add_field(msg, MIME_C_ENCR, mime->encoding->encoding_name);
			snprintf(buf, sizeof(buf), "%s/%s; charset=%s",
					 mime->mailcap->type_text, mime->mailcap->subtype_text,
					 mime->charset->charset_name);
			add_field(msg, MIME_C_TYPE, buf);
		} else {
			add_field(msg, MIME_C_ENCR,
					  supp_encodings[def_encoding].encoding_name);
			snprintf(buf, sizeof(buf), "%s/%s; charset=%s",
					 mailcap[DEFAULT_MAILCAP].type_text,
					 mailcap[DEFAULT_MAILCAP].subtype_text,
					 supp_charsets[def_charset].charset_name);
			add_field(msg, MIME_C_TYPE, buf);
		}
	}

	/* remove all meaningless parts */
	mprev = mime = msg->mime;

	while(mime) {
		if((mime->flags & DELETE_PART) ||
		   (!(mime->flags & ATTACHMENT) && !(mime->flags & TEXT_PART))) {
			if(mime == msg->mime) {
				msg->mime = mime->mime_next;
				mime->mime_next = NULL;
				discard_mime(mime);
				mprev = mime = msg->mime;
				continue;
			} else {
				mprev->mime_next = mime->mime_next;
				mime->mime_next = NULL;
				discard_mime(mime);
				mime = mprev;
			}
		}

		mprev = mime;
		mime = mime->mime_next;
	}

	mime = msg->mime;
	if(mime == NULL)
		goto rewrite;

	/* convert message to multipart */
	if(mime->mime_next != NULL) {
		time_t lt = time(NULL);
		int multitype = MULTIPART_MAILCAP;  /* multipart/mixed by default */
		struct _mime_msg *mm;
		char id[64];

		/* determine the type of multipart */
		while(mime) {
			if(mime->mailcap->subtype_code == CSUBTYPE_PGPSIG) {
				multitype = MULTIPART_SIGNED;   /* multipart signed */
				break;
			}

			if(mime->mailcap->subtype_code == CSUBTYPE_PGPENC) {
				multitype = MULTIPART_ENCRYPTED;    /* multipart/encrypted */
				break;
			}

			mime = mime->mime_next;
		}
		mime = msg->mime;

		strftime(id, 31, "%Y%m%d%H%M%S", localtime(&lt));
		snprintf(boundary, sizeof(buf), "_=XFMail.%s.%s:%s:%d=_", VERSION,
				 XF_OS, id, (int) getpid());
		switch(multitype) {
			case MULTIPART_ENCRYPTED:
				snprintf(buf, sizeof(buf),
						 "%s/%s; boundary=\"%s\"; protocol=\"%s/%s\"",
						 mailcap[MULTIPART_ENCRYPTED].type_text,
						 mailcap[MULTIPART_ENCRYPTED].subtype_text, boundary,
						 mailcap[PGPENC_MAILCAP].type_text,
						 mailcap[PGPENC_MAILCAP].subtype_text);
				break;

			case MULTIPART_SIGNED:
				snprintf(buf, sizeof(buf),
						 "%s/%s; boundary=\"%s\"; micalg=pgp-md5; protocol=\"%s/%s\"",
						 mailcap[MULTIPART_SIGNED].type_text,
						 mailcap[MULTIPART_SIGNED].subtype_text, boundary,
						 mailcap[PGPSIG_MAILCAP].type_text,
						 mailcap[PGPSIG_MAILCAP].subtype_text);
				break;

			default:
			case MULTIPART_MAILCAP:
				snprintf(buf, sizeof(buf), "%s/%s; boundary=\"%s\"",
						 mailcap[MULTIPART_MAILCAP].type_text,
						 mailcap[MULTIPART_MAILCAP].subtype_text, boundary);
				break;
		}

		add_field(msg, MIME_C_TYPE, buf);
		sprintf(id, "%02d", MIME_VERS_SUPP);
		id[2] = id[1];
		id[1] = '.';
		id[3] = '\0';
		add_field(msg, MIME_VERSION, id);

		mm = mime;
		while(mm) {
			if(mm->boundary)
				free(mm->boundary);
			else
				mm->flags |= MSG_BODY;

			mm->boundary = strdup(boundary);

			mm = mm->mime_next;
		}

		/* add preamble */
		mm = create_mime();
		mm->flags = PREAMBLE;
		mm->boundary = strdup(boundary);
		mm->mime_next = msg->mime;
		msg->mime = mm;

		/* add epilog */
		mm = create_mime();
		mm->flags = EPILOG;
		mm->boundary = strdup(boundary);
		mprev = msg->mime;
		while(mprev->mime_next)
			mprev = mprev->mime_next;
		mprev->mime_next = mm;

		/* make text part to be the first attachment after the preamble */
		/* and add MIME specific fields to it */
		if((mm = get_text_part(msg)) != NULL) {

			hf = mm->m_fields;
			while(hf) {
				hf1 = hf->next_head_field;
				if(hf->f_line)
					free(hf->f_line);
				free(hf);
				hf = hf1;
			}

			mm->m_fields = NULL;
			snprintf(buf, sizeof(buf), "%s/%s; charset=%s",
					 mm->mailcap->type_text, mm->mailcap->subtype_text,
					 mm->charset->charset_name);
			add_mime_field(mm, MIME_C_TYPE, buf);
			add_mime_field(mm, MIME_C_ENCR, mm->encoding->encoding_name);

			if(msg->mime->mime_next != mm) {
				mprev = msg->mime;
				while(mprev) {
					if(mprev->mime_next == mm) {
						mprev->mime_next = mm->mime_next;
						mm->mime_next = msg->mime->mime_next;
						msg->mime->mime_next = mm;
						break;
					}
					mprev = mprev->mime_next;
				}
			}
		}

	} else {
		/* only one attachment */
		if(!is_mime_text(mime)) {
			sprintf(buf, "%02d", MIME_VERS_SUPP);
			buf[2] = buf[1];
			buf[1] = '.';
			buf[3] = '\0';
			add_field(msg, MIME_VERSION, buf);

			hf = mime->m_fields;
			while(hf) {
				add_field(msg, hf->f_name, hf->f_line);
				hf = hf->next_head_field;
			}
		} else {
			sprintf(buf, "%02d", MIME_VERS_SUPP);
			buf[2] = buf[1];
			buf[1] = '.';
			buf[3] = '\0';
			add_field(msg, MIME_VERSION, buf);

			add_field(msg, MIME_C_ENCR, mime->encoding->encoding_name);
			snprintf(buf, sizeof(buf), "%s/%s; charset=%s",
					 mime->mailcap->type_text, mime->mailcap->subtype_text,
					 mime->charset->charset_name);
			add_field(msg, MIME_C_TYPE, buf);
		}
	}

	/* rewrite the whole message */
	rewrite:strcpy(buf, get_temp_file("mime"));

	if((mfd = fopen(buf, "w")) == NULL) {
		discard_mime(msg->mime);
		msg->mime = NULL;
		msg->status |= H_SHORT;
		msg->get_header(msg);
		display_msg(MSG_WARN, "MIME", "Can not open %s", buf);
		return -1;
	}

	print_message_header(msg, mfd);
	fflush(mfd);
	hlen = ftell(mfd);
	mime = msg->mime;
	while(mime) {
		if(mime->flags == 0) {
			mime = mime->mime_next;
			continue;
		}

		if(write_part(mime, msg, mfd) == -1) {
			discard_mime(msg->mime);
			msg->mime = NULL;
			display_msg(MSG_WARN, "MIME", "Failed to update MIME message");
			unlink(buf);
			msg->status |= H_SHORT;
			msg->get_header(msg);
			return -1;
		}

		fflush(mfd);
		/*if(ftell(mfd) > MAX_MSG_LEN) {
		   discard_mime(msg->mime);
		   msg->mime = NULL;
		   display_msg(MSG_WARN, "MIME", "Message can not be bigger then %d Kb", MAX_MSG_LEN/1024);
		   unlink(buf);
		   msg->status |= H_SHORT;
		   msg->get_header(msg);
		   return -1;
		   } */

		mime = mime->mime_next;
	}

	fflush(mfd);
	msg->msg_len = ftell(mfd);
	fclose(mfd);

	msg->header->header_len = hlen;

	do_move(buf, msg->get_file(msg));
	discard_mime(msg->mime);
	msg->mime = NULL;

	unlink(buf);
	touch_message(msg);
	cache_msg(msg);
	msg->status |= MMODIFIED;

	return 0;
}

void load_mailcap() {
	FILE *mfd;
	char mcapfile[255], buf[255];
	char type[16], subtype[16], ext[5];
	char tmp[33];
	struct _mime_mailcap *mcap;
	char *p;
	int checkglobal = 1;

	startmcapload:
	if(checkglobal && ((mfd = fopen(GLOBAL_MCAP, "r")) != NULL))
		checkglobal = 2;
	else {
		snprintf(mcapfile, sizeof(mcapfile), "%s/.xfmime", configdir);
		if((mfd = fopen(mcapfile, "r")) == NULL)
			return;
		checkglobal = 0;
	}

	while(fgets(buf, 254, mfd)) {
		strip_newline(buf);

		type[0] = subtype[0] = ext[0] = '\0';

		if(sscanf(buf, "%s %s", tmp, ext) != 2)
			continue;

		if((p = strrchr(tmp, '/')) == NULL)
			continue;

		*p = '\0';
		p++;

		if((strlen(p) > 16) || (strlen(tmp) > 16))
			continue;

		if((strlen(p) < 1) || (strlen(tmp) < 1))
			continue;

		strcpy(type, tmp);
		strcpy(subtype, p);

		if((p = strchr(buf, ';')) == NULL)
			continue;

		p++;
		while(*p == ' ')
			p++;

		if(strlen(p) <= 1)
			continue;

		mcap = find_mailcap(type, subtype, 1);

		if(mcap->process)
			continue;

		if(mcap->ext_mcap)
			free(mcap->ext_mcap);

		mcap->ext_mcap = strdup(p);

		if((strlen(ext) > 1) && strcmp(ext, "xxx") && (strlen(ext) < 5))
			strcpy(mcap->ext, ext);

		add_mailcap(mcap);
		discard_mcap(mcap);
	}

	fclose(mfd);

	if(checkglobal) {
		checkglobal = 0;
		goto startmcapload;
	}

	return;
}

void save_mailcap() {
	FILE *mfd;
	char mcapfile[255], buf[255];
	int i;

	if(readonly)
		return;

	snprintf(mcapfile, sizeof(mcapfile), "%s/.xfmime", configdir);
	if((mfd = fopen(mcapfile, "w")) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not open %s", mcapfile);
		return;
	}

	i = 0;
	while(mailcap[i].type_code != CTYPE_UNSUPPORTED) {
		if(mailcap[i].ext_mcap == NULL) {
			i++;
			continue;
		}

		snprintf(buf, sizeof(buf), "%s/%s %s ; %s\n", mailcap[i].type_text,
				 mailcap[i].subtype_text,
				 (strlen(mailcap[i].ext) > 1) ? mailcap[i].ext : "xxx",
				 mailcap[i].ext_mcap);
		fputs(buf, mfd);

		i++;
	}

	fclose(mfd);
}

/* TEST section */

void print_mime(struct _mime_msg *mime) {
	struct _head_field *fld;

	printf("---Start MIME structure------\n");
	if(!mime) {
		printf("NULL MIME structure\n");
		return;
	}

	printf("Start: %lu End: %lu\n", mime->m_start, mime->m_end);
	printf("Source: %s\n", mime->src_info ? mime->src_info : "none");
	printf("MIME version: %d\n", mime->mime_vers);
	if(mime->mailcap)
		printf("Type: %s/%s\n", mime->mailcap->type_text,
			   mime->mailcap->subtype_text);
	else
		printf("No mailcap\n");

	if(mime->encoding)
		printf("Encoding: %s\n", mime->encoding->encoding_name);
	else
		printf("No encoding\n");

	if(mime->charset)
		printf("Charset: %s\n", mime->charset->charset_name);
	else
		printf("No charset\n");

	printf("Content-ID: %s\n", mime->c_id ? mime->c_id : "none");
	printf("Content-Description: %s\n",
		   mime->c_descr ? mime->c_descr : "none");
	fld = mime->m_fields;
	if(fld) {
		printf("   ------MIME fields----\n");
		while(fld) {
			printf("%s: %s\n", fld->f_name, fld->f_line);
			fld = fld->next_head_field;
		}
		printf("   ------End of MIME fields----\n");
	} else
		printf("No MIME fields\n");

	printf("Boundary: %s\n", mime->boundary ? mime->boundary : "none");
	printf("Flags: %d\n", mime->flags);

	if(mime->mime_next) {
		printf("   ----Next structure follows:\n");
		print_mime(mime->mime_next);
	}

	printf("---End MIME structure--------\n");

	return;
}

int get_charset_pos(char *ch_name) {
	int i;

	i = 0;
	while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
		if(!strcasecmp(ch_name, supp_charsets[i].charset_name))
			return i;
		i++;
	}

	return -1;
}

int charset_code_from_name(char *ch_name) {
	int i;

	i = 0;
	while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
		if(!strcasecmp(ch_name, supp_charsets[i].charset_name))
			return supp_charsets[i].charset_code;
		i++;
	}

	return -1;
}

const char *charset_name_from_code(int code) {
	int i;

	for (i = 0; supp_charsets[i].charset_code != CHAR_UNKNOWN; i++)
		if (supp_charsets[i].charset_code == code)
			return supp_charsets[i].charset_name;
	return NULL;
}

const char *charset_descr_from_code(int code) {
	int i;

	for (i = 0; supp_charsets[i].charset_code != CHAR_UNKNOWN; i++)
		if (supp_charsets[i].charset_code == code)
			return supp_charsets[i].charset_descr;
	return NULL;
}


int is_charset_alias(char *ch_name) {
	int i, ch_code;

	if((ch_code = charset_code_from_name(ch_name)) == -1)
		return -1;

	i = 0;
	while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
		if(supp_charsets[i].charset_code == ch_code) {
			if(strcasecmp(supp_charsets[i].charset_name, ch_name))
				return i + 1;
			else
				return 0;
		}
		i++;
	}

	return 0;
}

int add_charset(char *ch_name, char *ch_descr, int ch_code) {
	int i, ch_alias, aliases;

	if(!ch_name)
		return -1;

	if(charset_code_from_name(ch_name) != -1) {
		display_msg(MSG_WARN, "add charset", "Charset already exists");
		return -1;
	}

	i = 0;
	ch_alias = -1;
	aliases = 0;
	if(ch_code < 0) {
		while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
			if(supp_charsets[i].charset_code >= ch_code)
				ch_code = supp_charsets[i].charset_code + 1;
			i++;
		}
	} else {
		while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
			if(supp_charsets[i].charset_code == ch_code) {
				aliases++;
				if(ch_alias == -1)
					ch_alias = i;
			}
			i++;
		}

		if(ch_alias == -1) {
			display_msg(MSG_WARN, "add charset",
						"Can not add alias to non-existent charset");
			return -1;
		}

		if(aliases > 8) {
			display_msg(MSG_WARN, "add charset",
						"Can not have more then 8 aliases of the same charset");
			return -1;
		}
	}

	if((i + 1) >= MAX_CHARSETS) {
		display_msg(MSG_WARN, "add charset", "Too many charsets");
		return -1;
	}

	supp_charsets[i + 1] = supp_charsets[i];

	supp_charsets[i].charset_code = ch_code;
	supp_charsets[i].charset_name = strdup(ch_name);
	supp_charsets[i].charset_descr = ch_descr ? strdup(ch_descr) : NULL;
	supp_charsets[i].decode =
	ch_alias >= 0 ? supp_charsets[ch_alias].decode : NULL;
	supp_charsets[i].encode =
	ch_alias >= 0 ? supp_charsets[ch_alias].encode : NULL;
	supp_charsets[i].flags = 0;

	return 0;
}

int del_charset(char *ch_name) {
	int i, k, ch_code, alias;

	if(!ch_name)
		return -1;

	if((ch_code = charset_code_from_name(ch_name)) == -1)
		return -1;

	alias = is_charset_alias(ch_name);

	i = 0;
	while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
		if(supp_charsets[i].charset_code == ch_code) {
			if(alias
			   && strcasecmp(ch_name, supp_charsets[i].charset_name)) {
				i++;
				continue;
			}

			if(supp_charsets[i].flags & CHARSET_FIXED) {
				if(alias) {
					display_msg(MSG_WARN, "delete charset alias",
								"This is a predefined alias\ncan not delete it");
					return -1;
				}
				i++;
				continue;
			}

			if(supp_charsets[i].charset_name)
				free(supp_charsets[i].charset_name);

			if(supp_charsets[i].charset_descr)
				free(supp_charsets[i].charset_descr);

			k = i;
			while(supp_charsets[k].charset_code != CHAR_UNKNOWN) {
				supp_charsets[k] = supp_charsets[k + 1];
				k++;
			}
			continue;
		}
		i++;
	}

	return 0;
}

int assemble_partial(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _mail_msg *msg1;
	struct _mime_mailcap *mcap;
	char id[64], *p, msg_file[255], buf[255];
	u_int number, total, h_printed;
	struct _head_field *fld;
	char ttm[32];
	long pos;
	FILE *mf, *pf;
	long nnum;

	if(!msg || !mime)
		return -1;

	if((nnum = get_new_name(ftemp)) == -1) {
		display_msg(MSG_WARN, "assemble", "No space in %s", FTEMP);
		return -1;
	}

	snprintf(msg_file, sizeof(msg_file), "%s/%ld", ftemp->fold_path, nnum);

	if((fld = find_mime_field(mime, MIME_C_TYPE)) == NULL) {
		display_msg(MSG_WARN, "Assemble", "Can not find %s", MIME_C_TYPE);
		return -1;
	}

	if((p = get_fld_param(fld, "id")) == NULL) {
		display_msg(MSG_WARN, "Assemble", "Can not find ID string");
		return -1;
	}
	snprintf(id, sizeof(id), "%s", p);

	if((p = get_fld_param(fld, "total")) == NULL) {
		display_msg(MSG_WARN, "Assemble",
					"Can not find total number of parts");
		return -1;
	}

	total = atoi(p);
	if(total <= 1) {
		display_msg(MSG_WARN, "Assemble", "Invalid total number");
		return -1;
	}

	mf = NULL;

	for(number = 1; number <= total; number++) {
		msg1 = msg->folder->messages;
		while(msg1) {
			if((mcap = get_mailcap_entry(msg1, NULL)) == NULL) {
				msg1 = msg1->next;
				continue;
			}

			if((mcap->type_code != CTYPE_MESSAGE) ||
			   (mcap->subtype_code != CSUBTYPE_PARTIAL)) {
				discard_mcap(mcap);
				msg1 = msg1->next;
				continue;
			}

			if((fld = find_field(msg1, MIME_C_TYPE)) == NULL) {
				msg1 = msg1->next;
				continue;
			}

			if((p = get_fld_param(fld, "id")) == NULL) {
				msg1 = msg1->next;
				continue;
			}

			if(!strcmp(id, p)) {
				if((p = get_fld_param(fld, "number")) == NULL) {
					msg1 = msg1->next;
					continue;
				}
				if(number == atoi(p))
					break;
			}
			msg1 = msg1->next;
		}

		if(msg1 == NULL) {
			display_msg(MSG_WARN, "Assemble",
						"Can not find part %d.\nMake sure that all parts are in the same folder",
						number);
			if(mf) {
				fclose(mf);
				unlink(msg_file);
			}

			return -1;
		}

		if(number == 1) {

			if((mf = fopen(msg_file, "w")) == NULL) {
				display_msg(MSG_WARN, "Assemble", "Can not open %s",
							msg_file);
				return -1;
			}

			fld = msg1->header->other_fields;
			while(fld) {
				if(strncasecmp(fld->f_name, "Content-", 8) &&
				   strncasecmp(fld->f_name, "Message-ID", 10) &&
				   strncasecmp(fld->f_name, "Encrypted", 9))
					print_header_field(fld, mf, 0);
				fld = fld->next_head_field;
			}

			if((pf = fopen(msg1->get_file(msg1), "r")) == NULL) {
				display_msg(MSG_WARN, "Assemble", "Can not read %s",
							msg1->get_file(msg1));
				return -1;
			}

			fseek(pf, msg1->header->header_len, SEEK_SET);
			pos = ftell(pf);
			h_printed = 0;
			while(fgets(buf, 255, pf)) {
				strip_newline(buf);

				if(strlen(buf) < 2)
					break;

				if(h_printed && ((buf[0] == ' ') || (buf[0] == 0x09))) {
					fputs(buf, mf);
					continue;
				}

				h_printed = 0;

				if((fld = get_field(buf)) == NULL) {
					fseek(pf, pos, SEEK_SET);
					break;
				}

				pos = ftell(pf);
				if(strncasecmp(fld->f_name, "Content-", 8) &&
				   strncasecmp(fld->f_name, "Message-ID", 10) &&
				   strncasecmp(fld->f_name, "Encrypted", 9) &&
				   strncasecmp(fld->f_name, "MIME-Version", 13)) {
					if(fld->f_line)
						free(fld->f_line);
					free(fld);
					continue;
				}

				print_header_field(fld, mf, 0);
				h_printed = 1;
				if(fld->f_line)
					free(fld->f_line);
				free(fld);
			}

			fprintf(mf, "%s: %04X\n", STATUS_FIELD, UNREAD);

			if(!find_field(msg1, "Date")) {
#ifdef HAVE_SETLOCALE
				setlocale(LC_TIME, "C");
#endif
				strftime(ttm, 31, "%a, %d %h %Y %T %Z",
						 localtime(&msg1->header->rcv_time));
#ifdef HAVE_SETLOCALE
				setlocale(LC_TIME, "");
#endif
				fprintf(mf, "Date: %s\n", ttm);
			}

			print_addr(msg1->header->Sender, "Sender", mf, 0);
			print_addr(msg1->header->From, "From", mf, 0);
			print_addr(msg1->header->To, "To", mf, 0);
			if(msg1->header->News)
				print_news_addr(msg1->header->News, "Newsgroups", mf);
			if(msg1->header->Subject)
				fprintf(mf, "Subject: %s\n", msg1->header->Subject);

			print_addr(msg1->header->Cc, "Cc", mf, 0);
			print_addr(msg1->header->Bcc, "Bcc", mf, 0);
			fputc('\n', mf);

			while(fgets(buf, 255, pf))
				fputs(buf, mf);

			fclose(pf);
		} else
			print_message_body(msg1, mf);
	}

	fclose(mf);
	if((msg1 = get_message(nnum, ftemp)) == NULL) {
		display_msg(MSG_WARN, "Assemble",
					"Can not parse assembled message");
		unlink(msg_file);
		return -1;
	}

	msg1->flags |= (M_TEMP | UNREAD);
	msg1->data = MSG_DAT_ENCAP;
	msg1->pdata = (void *) msg;
	msg->status |= LOCKED;
	mime->flags |= LOCK_PART;

	view_msg(msg1, 1);

	return 0;
}

int view_external(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _head_field *fld;
	char *p;
	char url[255], buf[255], method_str[16];
	u_int method;
	time_t expir;
	u_long size;
	u_int perm;
	struct _proc_info pinfo;
    string sFtpProg;

	if(!msg || !mime)
		return -1;

#define EXT_UNK     0x00
#define EXT_FTP     0x01
#define EXT_AFTP    0x02
#define EXT_TFTP    0x03
#define EXT_AFS     0x04
#define EXT_LOCAL   0x05
#define EXT_MAIL    0x06
#define EXT_EXT     0x07
#define EXT_CID     0x08

#define PERM_READ   0x01
#define PERM_RDWR   0x02

	if((fld = find_mime_field(mime, MIME_C_TYPE)) == NULL) {
		display_msg(MSG_WARN, "View external body", "Can not find %s",
					MIME_C_TYPE);
		return -1;
	}

	if((p = get_fld_param(fld, "access-type")) == NULL) {
		display_msg(MSG_WARN, "View external body",
					"Can not determine access method");
		return -1;
	}

	method = EXT_UNK;
	snprintf(method_str, sizeof(method_str), "%s", p);

	if(!strcasecmp(p, "FTP"))
		method = EXT_FTP;
	else if(!strcasecmp(p, "ANON-FTP"))
		method = EXT_AFTP;
	else if(!strcasecmp(p, "TFTP"))
		method = EXT_TFTP;
	else if(!strcasecmp(p, "AFS"))
		method = EXT_AFS;
	else if(!strcasecmp(p, "LOCAL-FILE"))
		method = EXT_LOCAL;
	else if(!strcasecmp(p, "MAIL-SERVER"))
		method = EXT_MAIL;
	else if(!strcasecmp(p, "CONTENT_ID"))
		method = EXT_CID;
	else if(!strncasecmp(p, "X-", 2))
		method = EXT_EXT;

	expir = 0L;
	size = 0L;
	perm = PERM_READ;

	if((p = get_fld_param(fld, "expiration")) != NULL) {
		expir = get_date(p);
		if(expir == 0L)
			display_msg(MSG_WARN, "View external body", "Invalid date %s",
						p);
		if(expir > time(NULL))
			display_msg(MSG_WARN, "Existense of the message after",
						"%s is not guaranteed", p);
	}

	if((p = get_fld_param(fld, "size")) != NULL)
		size = atol(p);

	if((p = get_fld_param(fld, "permission")) != NULL) {
		if(!strcasecmp(p, "read"))
			perm = PERM_READ;
		else if(!strcasecmp(p, "read-write"))
			perm = PERM_RDWR;
		else
			display_msg(MSG_WARN, "View external body",
						"Unknown permission type %s. Assuming \'read\'",
						p);
	}


	url[0] = '\0';

	switch(method) {
		case EXT_FTP:
		case EXT_TFTP:
		case EXT_AFTP:
			if((p = get_fld_param(fld, "site")) == NULL) {
				display_msg(MSG_WARN, "View external body",
							"Can not find FTP site name");
				break;
			}

			snprintf(url, sizeof(url), "%s://%s",
					 (method == EXT_TFTP) ? "tftp" : "ftp", p);
			if((p = get_fld_param(fld, "directory")) != NULL) {
				if(p[0] != '/')
					strcat(url, "/");
				strcat(url, p);
			} else
				strcat(url, "/");

			if((p = get_fld_param(fld, "name")) == NULL) {
				display_msg(MSG_WARN, "View external body",
							"Can not find file name to FTP");
				break;
			}

			if(p[0] != '/')
				strcat(url, "/");
			strcat(url, p);

			if((p = get_fld_param(fld, "mode")) != NULL)
				snprintf(buf, sizeof(buf), "%s transfer mode", p);
			else
				snprintf(buf, sizeof(buf), "assuming binary transfer mode");

			if(!display_msg
			   (MSG_QUEST, "Body of this message located at",
				"%s (%s), retrieve?", url, buf))
				break;

			sFtpProg = Config.get("ftprog", "");
			if(strstr(sFtpProg.c_str(), "%s") == NULL)
				snprintf(buf, sizeof(buf), "%s %s", sFtpProg.c_str(), url);
			else
				snprintf(buf, sizeof(buf), sFtpProg.c_str(), url, url);

			init_pinfo(&pinfo);
			pinfo.wait = WAIT_ASYNC;
			exec_child(buf, &pinfo);

			break;

		case EXT_LOCAL:
			if((p = get_fld_param(fld, "name")) == NULL) {
				display_msg(MSG_WARN, "View external body",
							"Can not find file name");
				break;
			}

			if(access(p, R_OK) == -1) {
				display_msg(MSG_WARN, "View external body",
							"Can not access %s", p);
			}

			init_pinfo(&pinfo);
			pinfo.wait = WAIT_ASYNC;
			pinfo.u_data = strdup(p);
			pinfo.ul_data = 0L;
			pinfo.handle = view_local_exit;

			if(file_view(p, &pinfo) == -1)
				view_local_exit(&pinfo);
			break;

		case EXT_AFS:
			p = get_fld_param(fld, "site");
			snprintf(url, sizeof(url), "file://%s", p ? p : "localhost");
			if((p = get_fld_param(fld, "name")) == NULL) {
				display_msg(MSG_WARN, "View external body",
							"Can not find file name");
				break;
			}

			if(p[0] != '/')
				strcat(url, "/");

			strcat(url, p);

			display_msg(MSG_WARN, "Body of message located at", "%s", url);
			break;


		case EXT_MAIL:
			{
				struct _mail_addr *ma;
				struct _mail_msg *msg1;
				FILE *mfd, *pfd;

				if((p = get_fld_param(fld, "server")) == NULL) {
					display_msg(MSG_WARN, "View external body",
								"Can not find mail-server address");
					break;
				}

				if((ma = get_address(p, ADDR_IGNORE_COMMAS)) == NULL) {
					display_msg(MSG_WARN, "View external body",
								"Illegal address %s", p);
					break;
				}

				p = get_fld_param(fld, "subject");
				if(!display_msg
				   (MSG_QUEST, "In order to get message body mail",
					"should be sent to %s", get_addr_line(ma)))
					break;

				if((msg1 = create_message(outbox)) == NULL) {
					display_msg(MSG_WARN, "View external body",
								"Can not create new message");
					break;
				}

				msg1->header->To = ma;

				if(p)
					msg1->header->Subject = strdup(p);

				if((pfd = fopen(msg->get_file(msg), "r")) == NULL) {
					display_msg(MSG_WARN, "MIME", "Can not open %s",
								msg->get_file(msg));
					break;
				}

				if(mime->boundary) {
					fseek(pfd, mime->m_start, SEEK_SET);
					while(fgets(buf, 255, pfd)) {
						strip_newline(buf);
						if(strlen(buf) < 1)
							break;
					}
				} else
					fseek(pfd, msg->header->header_len, SEEK_SET);

				while(fgets(buf, 255, pfd)) {
					strip_newline(buf);
					if(strlen(buf) < 1)
						break;
				}

				if(ferror(pfd) || feof(pfd)) {
					display_msg(MSG_WARN, "MIME", "Can not find message body");
					fclose(pfd);
					break;
				}

				if((mfd = fopen(msg1->get_file(msg1), "w")) == NULL) {
					display_msg(MSG_WARN, "MIME", "Can not open file %s",
								msg1->get_file(msg1));
					break;
				}

				print_message_header(msg1, mfd);

				fflush(mfd);
				msg1->header->header_len = ftell(mfd);

				while((ftell(mfd) < mime->m_end) && fgets(buf, 255, pfd)) {
					strip_newline(buf);
					if(strlen(buf) < 1)
						break;
					fputs(buf, mfd);
				}
				fflush(mfd);
				msg1->msg_len = ftell(mfd);

				fclose(mfd);
				fclose(pfd);

				if(send_message(msg1) == -1) {
					display_msg(MSG_WARN, "MIME", "Failed to send message");
					break;
				}
			}
			break;

		case EXT_EXT:
			display_msg(MSG_WARN, "View external body",
						"Unknown transfer protocol %s", method_str);
			break;

		case EXT_CID:
			{
				struct _mime_msg *mime1;

				if(mime->c_id == NULL) {
					display_msg(MSG_WARN, "View external body",
								"Content-ID missing");
					break;
				}
				mime1 = msg->mime;
				while(mime1) {
					if((mime1 != mime) && mime1->c_id
					   && (mime1->flags & ATTACHMENT)
					   && !strcmp(mime->c_id, mime1->c_id)) break;

					mime1 = mime1->mime_next;
				}
				if(mime1 == NULL) {
					display_msg(MSG_WARN, "View external body",
								"Can not find referenced part");
					break;
				}

				view_part(msg, mime1);
			}
			break;

		case EXT_UNK:
			display_msg(MSG_WARN, "View external body",
						"Can not determine transfer protocol");
			break;
	}


	return 0;
}

char *rfc1522_decode(char *string, int *charset) {
	static char buf[255];
	char dec[255];
	char *enc, *body;
	char *p, *p1, *p2;
	int ecode, i, was_word;

	if(!string)
		return NULL;

	if(strlen(string) > 200)
		return string;

	p = string;
	buf[0] = '\0';
	i = 0;
	was_word = 0;

	while((p1 = strstr(p, "=?")) != NULL) {
		*p1 = '\0';
		if(was_word) {
			p2 = p;
			while(*p2 != '\0') {
				if((*p2 != ' ') && (*p2 != 0x09)) {
					strcat(buf, p);
					break;
				}
				p2++;
			}
		} else
			strcat(buf, p);

		was_word = 0;

		*p1 = '=';
		p1++;

		if(((p2 = strstr(p1, "?Q?")) == NULL) &&
		   ((p2 = strstr(p1, "?q?")) == NULL) &&
		   ((p2 = strstr(p1, "?B?")) == NULL) &&
		   ((p2 = strstr(p1, "?b?")) == NULL)) {
			enc = p1;
			p2 = NULL;
			goto err;
		}

		p2 += 3;
		if((p2 = strstr(p2, "?=")) == NULL) {
			enc = p1;
			p2 = NULL;
			goto err;
		}

		if((p2 - p1) < 7)
			goto err;

		*p2 = '\0';
		*p1 = '\0';
		p1++;

		strcpy(dec, p1);
		p1--;
		*p2 = '?';
		*p1 = '?';

		if((enc = strchr(dec, '?')) == NULL)
			goto err;

		*enc = '\0';
		enc++;

		if((strlen(enc) < 1) || (strlen(dec) < 3))
			goto err;

		enc[1] = '\0';
		body = enc + 2;

		if(strlen(body) < 1)
			goto err;

		if(charset && (*charset == -1))
			*charset = get_charset_pos(dec);

		if((*enc == 'q') || (*enc == 'Q'))
			ecode = CE_QPRT;
		else if((*enc == 'b') || (*enc == 'B'))
			ecode = CE_BASE64;
		else
			goto err;

		if(ecode == CE_QPRT) {
			qprt_decode(NULL, &ecode);
			qprt_header = 1;
			p = qprt_decode(body, &ecode);
			qprt_header = 0;
		} else {
			base64_decode(NULL, &ecode);
			p = base64_decode(body, &ecode);
		}

		if(!p)
			goto err;

		strcat(buf, p);
		p = p2 + 2;
		i++;
		was_word = 1;
		continue;

		err:strcat(buf, "=");
		p = p1;
		was_word = 0;
		continue;
	}

	if(i == 0)
		return string;

	strcat(buf, p);

	return buf;
}


char *rfc1522_encode(char *string, int charset, int encoding) {
#define MAX_ENC_WORD_SIZE   75
	static char result[255];
	char enc[MAX_ENC_WORD_SIZE + 1];
	char *p, *p1, *p2;
	int len, encoded, split, was_word;

	if(!string)
		return NULL;

	if(strlen(string) > 200)
		return string;

	if(charset == -1)
		charset = def_charset;

	if(encoding == -1)
		encoding = CE_QPRT;

	p = string;

	encoded = 0;
	was_word = 0;
	result[0] = '\0';

	while(*p != '\0') {
		split = 0;
		p2 = p;
		while(*p2 == ' ')
			p2++;

		if((p1 = strchr(p2, ' ')) == NULL)
			len =
			(strlen(p) >
			 MAX_ENC_WORD_SIZE) ? MAX_ENC_WORD_SIZE : strlen(p);
		else {
			len =
			((p1 - p) >
			 MAX_ENC_WORD_SIZE) ? MAX_ENC_WORD_SIZE : (p1 - p);
			split = 1;
		}

		snprintf(enc, len + 1, "%s", p);
		p += len;
		p1 = enc;
		while(isascii(*p1) && (*p1 != '\0'))
			p1++;

		if(*p1 == '\0')
			goto err;

		if(encoding == CE_QPRT) {
			qprt_header = 1;
			p1 = qprt_encode(enc, len);
			qprt_header = 0;
		} else if(encoding == CE_BASE64)
			p1 = base64_encode(enc, len);
		else
			goto err;

		if(!p1)
			goto err;

		p2 = result + strlen(result);

		sprintf(p2, "%s=?%s?%c?%s?=", was_word ? " " : "",
				supp_charsets[charset].charset_name,
				(encoding == CE_QPRT) ? 'Q' : 'B', p1);
		encoded = 1;
		was_word = split;
		continue;

		err:strcat(result, enc);
		was_word = 0;
	}

	if(!encoded)
		return string;

	return result;
}

struct _mime_msg *attach_file(struct _mail_msg *msg, char *file,
							  struct _mime_mailcap *mcap, int encoding,
							  int flags) {
	struct _mime_msg *mime;
	struct stat sb;
	char *name, buf[255];
	u_int size;

	if(!msg || !file || (strlen(file) < 1))
		return NULL;

	if(msg->mime == NULL)
		mime_scan(msg);

	if(msg->mime == NULL)
		return NULL;

	if(stat(file, &sb) == -1)
		return NULL;

	size = (u_int) sb.st_size;

	if((mime = create_mime()) == NULL) {
		display_msg(MSG_WARN, "MIME", "Can not create new attachment");
		return NULL;
	}

	if(mcap)
		mime->mailcap = mcap;
	else
		mime->mailcap = &mailcap[BINARY_MAILCAP];

	if(encoding > 0)
		mime->encoding = &supp_encodings[encoding];
	else
		mime->encoding = &supp_encodings[BINARY_ENCODING];

	mime->src_info = strdup(file);
	mime->flags = ATTACHMENT;

	name = mime->src_info;
	if((name = strrchr(mime->src_info, '/')) != NULL)
		name++;
	else
		name = mime->src_info;

	if(mime->mailcap->type_code == CTYPE_TEXT) {
		if(flags & ATT_NOFILEINFO)
			snprintf(buf, sizeof(buf), "%s/%s; charset=%s",
					 mime->mailcap->type_text, mime->mailcap->subtype_text,
					 mime->charset->charset_name);
		else
			snprintf(buf, sizeof(buf),
					 "%s/%s; charset=%s; name=%s; SizeOnDisk=%d",
					 mime->mailcap->type_text, mime->mailcap->subtype_text,
					 mime->charset->charset_name, name, size);
	} else {
		if(flags & ATT_NOFILEINFO)
			snprintf(buf, sizeof(buf), "%s/%s", mime->mailcap->type_text,
					 mime->mailcap->subtype_text);
		else
			snprintf(buf, sizeof(buf), "%s/%s; name=%s; SizeOnDisk=%d",
					 mime->mailcap->type_text, mime->mailcap->subtype_text,
					 name, size);
	}

	add_mime_field(mime, MIME_C_TYPE, buf);
	add_mime_field(mime, MIME_C_ENCR, mime->encoding->encoding_name);
	if(!(flags & ATT_NODISPINFO)) {
		snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", name);
		add_mime_field(mime, MIME_C_DISP, buf);
	}

	mime->mime_next = msg->mime;
	msg->mime = mime;

	if(flags & ATT_NOUPDATE)
		return mime;

	if(update_mime(msg) == -1) {
		display_msg(MSG_WARN, "MIME", "Mime update failed");
		return NULL;
	}

	mime_scan(msg);
	return msg->mime;
}

void set_mime_descr(struct _mime_msg *mime, char *descr) {
	if(!mime || !descr || (strlen(descr) > 127))
		return;

	mime->c_descr = strdup(descr);
	add_mime_field(mime, MIME_C_DESCR, descr);

	return;
}

char *get_mime_fname(struct _mime_msg *mime) {
	char *mname;
	struct _head_field *hf;

	mname = NULL;
	if((hf = find_mime_field(mime, MIME_C_TYPE)) != NULL)
		mname = get_fld_param(hf, "name");

	if(mname == NULL) {
		if((hf = find_mime_field(mime, MIME_C_DISP)) != NULL)
			mname = get_fld_param(hf, "filename");
	}

	return mname;
}

u_long get_mime_fsize(struct _mime_msg * mime) {
	char *mname;
	struct _head_field *hf;

	if((hf = find_mime_field(mime, MIME_C_TYPE)) == NULL)
		return 0;

	mname = get_fld_param(hf, "SizeOnDisk");
	if(mname)
		return atol(mname);
	else
		return 0;
}

char *get_mime_line(int mnum, struct _mail_msg *msg,
					struct _mime_msg *mime) {
	static char buf[255];
	char mdescr[128];
	char mtype[64];
	char *mname, *mstr, *p;
	int msize, i;
	FILE *mfd;
	struct _head_field *hf = NULL;

	snprintf(mtype, sizeof(mtype), "%s/%s", mime->mailcap->type_text,
			 mime->mailcap->subtype_text);
	msize = get_mime_fsize(mime);
	mname = get_mime_fname(mime);

	if(((mstr = mime->c_descr) == NULL) &&
	   (mime->mailcap->type_code == CTYPE_MESSAGE)) {

		if((mfd = fopen(msg->get_file(msg), "r")) == NULL)
			goto nodigest;

		fseek(mfd, mime->m_start, SEEK_SET);
		if(mime->boundary) {
			while(fgets(buf, 255, mfd)) {
				if((*buf == '\r') || (*buf == '\n') || (*buf == '\0'))
					break;
			}
		}

		(*mime->encoding->ce_dec) (NULL, &i);
		while((ftell(mfd) < mime->m_end) && fgets(buf, 255, mfd)) {
			if((p = (*mime->encoding->ce_dec) (buf, &i)) == NULL)
				continue;
			if((*p == '\r') || (*p == '\n') || (*p == '\0'))
				break;
			if(!strncasecmp(p, "Subject: ", 9) &&
			   ((hf = get_field(p)) != NULL)) {
				mstr = hf->f_line;
				strip_newline(mstr);
				break;
			}
		}

		fclose(mfd);
	}

	nodigest:
	if(mname) {
		if(msize > 0)
			snprintf(mdescr, sizeof(mdescr), "%-12.12s %7d %s", mname,
					 msize, mstr ? mstr : "");
		else
			snprintf(mdescr, sizeof(mdescr), "%-12.12s %s", mname,
					 mstr ? mstr : "");
	} else
		snprintf(mdescr, sizeof(mdescr), "%s", mstr ? mstr : "");

	snprintf(buf, sizeof(buf), "%d %-22.22s %-10.10s %-10.10s %s", mnum,
			 mtype, mime->encoding->encoding_name,
			 mime->charset->charset_name, mdescr);

	if(hf) {
		if(hf->f_line)
			free(hf->f_line);
		free(hf);
	}

	return buf;
}
