
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "kernel.h"
#include "bbc.h"
#include "dbox.h"
#include "menu.h"
#include "werr.h"
#include "wimp.h"
#include "template.h"
#include "os.h"
#include "print.h"
#include "xferrecv.h"
#include "xfersend.h"
#include "flex.h"

#include "narc.h"
#include "hdrs.h"

#ifdef MemCheck
#include "MemCheck:Flex.h"
#endif



#define MAX_MULTIPART  1000


extern OPTIONS options;
extern int dbox_menu_field;
extern int menu_expected_persist;

extern char *content_encoding;
extern char *content_encoding_8bit;
extern char *content_quoted_printable;
extern char *content_encoding_base64;
extern char fname_export[128];

extern TEXTR text_rec_temp;

static void *menu_handle;

wimp_menustr *wmenu_attach;

char menu_get_name[64];
char temp_fname[48] = {0};

static char *base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char base64_decode[] = {
	62,0,0,0,63,
	52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
	0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
	15,16,17,18,19,20,21,22,23,24,25, 0, 0, 0, 0, 0,
	0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
	41,42,43,44,45,46,47,48,49,50,51
};




typedef struct {
	int  value;
	char *name;
} MNEMTAB;


static MNEMTAB ftype_names[] = {
	0xddc, "arc",
	0xddc, "zoo",
	0xddc, "zip",
	0xddc, "arj",
	0xddc, "lzh",
	0xddc, "hpk",

	0xc46, "tar",
	0xc32, "rtf",
	0xfff, "txt",
	/* 0xfff, "doc", */
	0xfff, "man",
	0xfff, "me",
	0xfaf, "htm",
	0xfaf, "html",
	0xffd, "dat",
	0xfeb, "bat",
	0xfb1, "wav",
	0x694, "mac",
	0x695, "gif",
	0x697, "pcx",
	0x69c, "bmp",
	0xadf, "pdf",
	0xc85, "jpg",
	0xc85, "jpeg",
	0xff0, "tif",
	0xff0, "tiff",
	0xff5, "ps",
	0xdea, "dxf",
	0xdfe, "csv",
	0xb28, "url",
	0xfff, "c",
	0xfff, "h",
	0xfff, "asm",
	0xfff, "text",
	0xddc, "sit",
	0xfe4, "bat",
	0xfe4, "exe",
	0xfe4, "com",
	0xfe4, "cmd",

	0xae6, "doc",  /* ms word */
	0xb2f, "wmf",  /* windows metafile */
	-1,NULL
};


static char *app_riscos = "application/riscos";
static char *mimemap_err = "MimeMap module is not loaded: ";
static int mimemap_err_flag = 0;
static char *encoding_names[] = {
	"Plain","Quoted","Rich text","UUE","Base64","Base64","8 Bit","","UUE","UUE","YENC","",""
};


int attach_start;
int attach_length;
int attach_end;
int attach_num;
int attach_encoding=0;   /* 0=UUE  1=base64 */
dbox dbox_save_attach=NULL;

int yenc_size;
int yenc_part;
int yenc_total;
int yenc_nparts;
int yenc_begin;
int yenc_end;
int yenc_end_size;
int yenc_end_part;
unsigned int yenc_crc;
unsigned int yenc_pcrc;
FILE *f_errlog;
char *fname_errlog = "<pluto$dir>.tmp.error_log";

// CRC routines for YENC ----------------------------------------


unsigned int crc_tab[256] =

{
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}
;

unsigned long crc_val;
long crc_anz;

void CrcInit()
{
	crc_val = -1L ;
	crc_anz=0L;
}



void CrcAdd(int c)
{
	unsigned long ch1,ch2,cc;


	/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
	/* for (i = 0; i < size; i++) */
	/*      crccode = crc32Tab[(int) ((crccode) ^ (buf[i])) & 0xff] ^  */
	/*                (((crccode) >> 8) & 0x00FFFFFFL); */
	/*   return(crccode); */


	cc= (c) & 0x000000ffL;
	ch1=(crc_val ^ cc) & 0xffL;
	ch1=crc_tab[ch1];
	ch2=(crc_val>>8L) & 0xffffffL;  // Correct version
	crc_val=ch1 ^ ch2;
	crc_anz++;
}

//-------------------------------------------------------------



static void make_boundary_string(TEXTR *t)
/*********************************/
{
	int  i;

	if(t->boundary_string_len > 0)
		return;

	strcpy(t->boundary_string,get_message_id(0,0));
	for(i=10; i<32; i++)
	{
		t->boundary_string[i] = base64[rand() % 64];
	}
	t->boundary_string[32]=0;
	t->boundary_string_len = 32;
}   /* end of make_boundary_string */



char *uudecode_line(char **anchor, int *offset, int *length, int encoding)
/******************************************************************/
{
	char *p;
	char *p2;
	int  n_bits;
	int  n_chars;
	int  outix;
	int  c;
	int  value;

	static int  acc;
	static char out[300];

	p = *anchor + *offset;

	acc = 0;
	n_bits = 0;
	outix = 0;
	n_chars = 0;

	switch(encoding)
	{
	case ENCODE_BASE64:
	case ENCODE_BASE64_SINGLE:
		n_chars = 0;
		while((c = *p++) != '\n')
		{
			if(c == '=') break;  /* ??? */

			if((c >= '+') && (c <= 'z'))
			{
				value = base64_decode[c - '+'];

				acc = (acc << 6) | value;
				n_bits += 6;

				if(n_bits >= 8)
				{
					n_bits -= 8;
					out[outix++] = acc >> n_bits;
					n_chars++;

					if((c == '=') || (outix >= sizeof(out)))
						break;
				}
			}
		}
		break;

	case ENCODE_YENC:
		if(memcmp(p,"=y",2)==0)
			return(NULL);

		while(((c = *p++) != '\n') && (c != '\r'))
		{
			if(c == '=')
			{
				/* escape character */
				c = *p++ - 64;
			}
			out[n_chars++] = (c-42) & 0xff;
		}
		break;

	case ENCODE_UUE:
	case ENCODE_MIMEUUE:
	case ENCODE_MIMEXUUE:
		if(memcmp(p,"end",3)==0)
			return(NULL);

		if(*p <= ' ')
			return(NULL);

		n_chars = (*p++ - ' ') & 0x3f;  /* number of output characters */

		while((c = *p++) != '\n')
		{
			value = (c - 0x20) & 0x3f;
			acc = (acc << 6) | value;
			n_bits += 6;

			if(n_bits >= 8)
			{
				n_bits -= 8;
				out[outix++] = acc >> n_bits;
				if((outix >= n_chars) || (outix >= sizeof(out)))
				{
					while((*p != '\n') && (*p != 0))
						p++;
					p++;
					break;
				}
			}
		}
		break;

	case ENCODE_QUOTED:
	case ENCODE_TEXT_NO_HEADER_Q:
		while(((c = *p++) != '\n') && (c != 0))
		{
			if(c == '=')
			{
				if(*p == '\n')
				{
					p++;
					continue;
				}
				if(isxdigit(p[0]) && isxdigit(p[1]))
				{
					c = (hexdig(p[0]) * 16) + hexdig(p[1]);
					p+=2;
				}
			}
			out[n_chars++] = c;

			if(n_chars>=sizeof(out))
			{
				break;
			}
		}
		if(n_chars < sizeof(out))
			out[n_chars++] = '\n';
		break;

	default:
	case ENCODE_TEXT_NO_HEADER:
		p2 = p;
		while(((c = *p++) != '\n') && (c != 0));
		*length = p - p2;
		*offset = p - *anchor;
		return(p2);
	}

	*offset = p - *anchor;
	*length = n_chars;
	return(out);
}   /* end of uudecode_line */




int uuencode_length(int length)
/*****************************/
/* Calculate total size of uuencode attachment */
{
	int  n_lines;
	int  remainder;
	int  total;

	n_lines = length / 45;
	remainder = length - (n_lines * 45);

	total = (n_lines * 63) + (remainder*4)/3;
	return(total + 33);    /* allow for \n\nbegin 644 filenamexx,fff  .... \nend\n */
}   /* end of uuencode_length */



static int b64encode_length(int length)
/*******************************/
/* Calculate total size of base64 encode attachment  45->60 characters per line */
{
	int  n_lines;
	int  remainder;
	int  total;

	n_lines = length / 45;
	remainder = length - (n_lines * 45);

	total = (n_lines * 61) + (remainder*4)/3;
	return(total);    /* allow header  */
}   /* end of b64encode_length */




static int encode_examine_file(int length, FILE *f)
/*************************************************/
/* Return:  bit 0  any long lines
            bit 1  any non-7bit chars
            bit 2  too many non-7bit chars, need base64 encoding */
{
	int  ix;
	int  c;
	int  line_length=0;
	int  count=0;
	int  long_lines=0;
	int  answer = 0;
	int  prev = '\n';
	int  c2;

	for(ix=0; ix<length; ix++)
	{
		c = fgetc(f);
		if(c == '\n')
		{
			if(line_length > 80)
				long_lines++;
			line_length = 0;
		}
		else if(((c < ' ') && (c != '\t') && (c != '\r')) || (c > 126))
		{
			count++;
		}

		if((c == '#') && (prev == '\n'))
		{
			/* check for #! rmail */
			if((c2 = fgetc(f)) == '!')
			{
				count++;
			}
			ungetc(c2,f);
		}
		prev = c;
	}
	if(line_length > 80)
		long_lines++;

	if(count > 0)
	{
		if(count > (length/8))
			answer = 6;
		else
			answer = 2;
	}
	if(long_lines)
		answer |= 1;

	return(answer);
}   /* end of encode_examine_file */




static int quote_encode_file(int length, FILE *f, char **base, int displ)
/***********************************************************************/
{
	int  ix;
	int  c='\n';
	int  prev_c;
	int  line_length = 0;
	int  displ1 = displ;
	char *out;
	char buf[16];

	if(base != NULL)
		out = *base + displ;

	for(ix=0; ix<length; ix++)
	{
		prev_c = c;
		c = fgetc(f);

		if(line_length > 80)
		{
			if(base != NULL)
			{
				out = *base + displ;
				out[0] = '=';
				out[1] = '\n';
				prev_c = '\n';
			}
			displ += 2;
			line_length = 0;
		}

		/* note: encode # at start of a line (as in #! rnews) */
		if((c >= 127) || (c == '=') || ((c < ' ') && (c != '\n')) ||
				((prev_c == '\n') && ((c == '#') || (c == '-'))  ))
		{
			if(base != NULL)
			{
				sprintf(buf,"%.2X",c);
				out = *base + displ;
				out[0] = '=';
				out[1] = buf[0];
				out[2] = buf[1];
			}
			displ += 3;
			line_length+=3;
		}
		else
		{
			if(c == '\n')
				line_length = 0;
			else
				line_length++;

			if(base != NULL)
			{
				out = *base + displ;
				*out = c;
			}
			displ++;
		}
	}
	if(base == NULL)
		return(displ - displ1);
	else
		return(displ);
}   /* end of quote_encode_file */




int uuencode_file(int length, FILE *f, char **base, int displ)
/************************************************************/
/* Write out uuencoded data, return pointer to after the end of the uue data */
{
	int  i;
	int  n_chars;
	int  acc;
	int  bbits;
	char *out;
	int  c;

	while(length > 0)
	{
		if(length >= 45)
			n_chars = 45;
		else
			n_chars = length;

		out = *base + displ;
		*out = n_chars + ' ';
		displ++;

		acc = 0;
		bbits=0;
		for(i=0; i<n_chars; i++)
		{
			acc = (acc << 8) + fgetc(f);
			bbits += 8;

			while(bbits >= 6)
			{
				c = ((acc >> (bbits-6)) & 0x3f) + ' ';
				if(c == ' ') c = '`';

				out = *base + displ;
				*out = c;
				displ++;

				bbits -= 6;
			}
		}
		while(bbits > 0)
		{
			acc = (acc << 8);
			bbits += 8;

			while(bbits >= 6)
			{
				c = ((acc >> (bbits-6)) & 0x3f) + ' ';
				if(c == ' ') c = '`';

				out = *base + displ;
				*out = c;
				displ++;

				bbits -= 6;
			}
		}

		out = *base + displ;
#ifdef deleted
		if(length > 0)
		{
			*out++ = 'a';
			displ++;
		}
#endif
		*out = '\n';
		displ++;

		length -= n_chars;
	}

	out = *base + displ;
	memcpy(out,"`\nend\n",6);
	displ += 6;
	return(displ);
}   /* end of uuencode_file */




static int b64encode_file(int length, FILE *f, char **base, int displ)
/********************************************************************/
/* Write out base64 encoded data, return pointer to after the end of the uue data */
{
	int  n_chars;
	int  acc;
	char *out;

	while(length > 0)
	{
		if(length >= 45)
			n_chars = 45;
		else
			n_chars = length;

		while(n_chars > 0)
		{
			acc = fgetc(f) << 16;
			if(n_chars > 1)
				acc += (fgetc(f) << 8);
			if(n_chars > 2)
				acc += fgetc(f);

			out = *base + displ;

			out[0] = base64[acc >> 18];
			out[1] = base64[(acc >> 12) & 0x3f];
			if(n_chars > 1)
				out[2] = base64[(acc >> 6) & 0x3f];
			else
				out[2] = '=';
			if(n_chars > 2)
				out[3] = base64[acc & 0x3f];
			else
				out[3] = '=';

			displ+=4;
			n_chars -= 3;
		}

		out = *base + displ;
		*out = '\n';
		displ++;

		length -= 45;
	}

	out = *base + displ;
	*out = '\n';
	displ++;
	return(displ);
}   /* end of b64encode_file */




static BOOL attach_saver(char *filename, void *handle)
/****************************************************/
{
	int  n_chars;
	char *p_out;
	int  total = 0;

	FILE *f_out;
	TEXTR *t;

	t = (TEXTR *)handle;

	f_out = fopen_werr(filename,"w","Attachment ");
	if(f_out == NULL)
	{
		return(FALSE);
	}

	while(attach_start < attach_end)
	{
		p_out = uudecode_line(&t->text_base,&attach_start,&n_chars,attach_encoding);
		if(p_out == NULL)
			break;
		if(fwrite_werr(p_out,1,n_chars,f_out) < 0)
			break;

		total += n_chars;
	}

	fclose(f_out);

	/* we now need to set the filetype, in case the file previously existed with
	   a different filetype */
	set_filetype(filename,t->attach[attach_num].ftype);

	return(TRUE);
}   /* end of attach_saver */




static BOOL attach_sender(void *handle, int *maxbuf1)
/***************************************************/
/* Send data */
{
	TEXTR *t;
	char *p_out;
	char *buf;
	int  bufix;
	int  n_chars;
	int  x;
	int  maxbuf = *maxbuf1;

	buf = malloc(maxbuf);
	if(buf == NULL) return(FALSE);
	bufix=0;

	t = (TEXTR *)handle;

	while(attach_start < attach_end)
	{
		p_out = uudecode_line(&t->text_base,&attach_start,&n_chars,attach_encoding);
		if(p_out == NULL)
			break;
		if((bufix + n_chars) > maxbuf)
		{
			x = maxbuf - bufix;
			memcpy(&buf[bufix],p_out,x);
			n_chars -= x;
			p_out += x;

			if(!xfersend_sendbuf(buf,bufix+x))
			{
				free(buf);
				return(FALSE);
			}
			bufix = 0;
		}

		memcpy(&buf[bufix],p_out,n_chars);
		bufix += n_chars;
	}
//_kernel_register_slotextend(flex_budge);


	if(bufix > 0)
	{
		if(!xfersend_sendbuf(buf,bufix))
		{
			free(buf);
			return(FALSE);
		}
	}

	free(buf);
	return(TRUE);
}   /* end of attach_sender */



int get_yenc_value(char *line, char *key, int *value)
/**********************************************/
/* Read decimal number after  key */
{
	char *p;

	if((p = strstr(line,key)) != NULL)
	{
		*value = atoi(&p[strlen(key)]);
		return(TRUE);
	}
	return(FALSE);
}   /* end of get_yenc_value */



int get_yenc_xvalue(char *line, char *key, unsigned int *value)
/********************************************************/
/* Read hexadecimal number after  key */
{
	char *p;

	if((p = strstr(line,key)) != NULL)
	{
		sscanf(&p[strlen(key)],"%x",value);
		return(TRUE);
	}
	return(FALSE);
}   /* end of get_yenc_xvalue */




static void attach_get_start(TEXTR *t, int a)
/*******************************************/
{
	int  i;
	int  start;
	int  end;
	int  c;
	char *p;
	char buf[300];

	start = t->attach[a].displ + 1;
	p = &t->text_base[start];

	attach_encoding = t->attach[a].encoding;

	switch(attach_encoding)
	{
	case ENCODE_MIMEXUUE:
		while((p[0] != '\n') || (p[1] != '\n'))
			p++;
		/* skip blank lines up to "begin" */
		while(*p == '\n')
			p++;

		start = p - t->text_base;
		/* drop through to next case */
	case ENCODE_UUE:
		/* skip attachment header */
		while(t->text_base[start++] != '\n');

		/* find end of attachment */
		for(end=start; end<t->text_length; end++)
		{
			if((t->text_base[end] == '\n') && (memcmp(&t->text_base[end+1],"end",3)==0))
				break;
		}
		break;

	case ENCODE_YENC:
		/* skip attachment header */

		yenc_size = 0;
		yenc_part = 0;
		yenc_total = 0;
		yenc_nparts = 0;
		yenc_begin = 0;
		yenc_end = 0;
		yenc_end_size = 0;
		yenc_end_part = 0;
		yenc_crc = 0;
		yenc_pcrc = 0;

		while(memcmp(&t->text_base[start],"=y",2)==0)
		{
			i=0;
			while((c = t->text_base[start++]) != '\n')
			{
				if(i < (sizeof(buf)-1))
					buf[i++] = c;
			}
			buf[i]=0;

			if(memcmp(buf,"=ybegin ",8)==0)
			{
				get_yenc_value(buf," size=",&yenc_size);
				get_yenc_value(buf," part=",&yenc_part);
				get_yenc_value(buf," total=",&yenc_total);
			}
			else if(memcmp(buf,"=ypart ",7)==0)
			{
				get_yenc_value(buf," begin=",&yenc_begin);
				get_yenc_value(buf," end=",&yenc_end);
			}
		}

		/* find end of attachment */
		for(end=start; end<t->text_length; end++)
		{
			if((t->text_base[end] == '\n') && (memcmp(&t->text_base[end+1],"=yend ",6)==0))
			{

				i=0;
				end++;
				while(((c = t->text_base[end+i]) != '\n') && (c != 0))
				{
					if(i < (sizeof(buf)-1))
						buf[i++] = c;
					else
						break;
				}
				buf[i]=0;

				get_yenc_value(buf," size=",&yenc_end_size);
				get_yenc_value(buf," part=",&yenc_end_part);
				get_yenc_xvalue(buf," pcrc32=",&yenc_pcrc);
				get_yenc_xvalue(buf," crc32=",&yenc_crc);

				break;
			}
		}
		break;

	case ENCODE_BASE64_SINGLE:
		start--;
		end = t->text_length;
		break;

	case ENCODE_TEXT_NO_HEADER:
	case ENCODE_TEXT_NO_HEADER_Q:
		end = t->attach[a].length + t->attach[a].displ - 1;
		break;

	default:
		/* MIME attachment */
		while((p[0] != '\n') || (p[1] != '\n'))
			p++;
		p+=2;

		start = p - t->text_base;
		end = t->attach[a].length  + t->attach[a].displ - 1;
		break;
	}
	if(end > t->text_length)  end = t->text_length;

	attach_end = end;
	attach_length = end - start;
	attach_start = start;
	attach_num = a;
}   /* end of attach_get_start */





static void attach_html_related(TEXTR *t, int a, char *fname)
/***********************************************************/
/* Look for related attachments which are referenced from this
   HTML attachment */
{
	int  i;
	int  ix;
	FILE *f;
	char *p;
	char *p_end;
	int  ref_length;
	int  cid;
	long int displ;
	long int displ_next;
	char buf[256];
	char ref[256];
	char attachments_used[100];

	memset(attachments_used,0,sizeof(attachments_used));

	for(i=0; i<t->attachments; i++)
	{
		if(i==a)
			continue;
		if(t->attach[i].content_id != 0)
			break;
	}

	if(i >= t->attachments)
		return;   /* no content-id in attachments */

	/* look through the HTML file for cid: references */
	f = fopen(fname,"r+");
	if(f == NULL)
		return;

	while(!feof(f))
	{
		displ = ftell(f);
		if(fgets(buf,sizeof(buf),f) != NULL)
		{
			displ_next = ftell(f);
			if((p = strstr(buf,"cid:")) != NULL)
			{
				p_end = strpbrk(p,">= \"\n\t");

				if(p_end != NULL)
				{
					ref_length = p_end-p-4;
					memcpy(&ref[1],&p[4],ref_length);
					ref[0]='<';
					ref[ref_length+1] = '>';
					ref[ref_length+2] = 0;

					cid = gethash32(ref);
					if(cid != 0)
					{
						for(ix=0; ix<t->attachments; ix++)
						{
							if(t->attach[ix].content_id == cid)
							{
								fseek(f,displ+(p-buf),SEEK_SET);
								fprintf(f,"cid%02d",ix);
								attachments_used[ix] = 1;

								for(i=1; i<ref_length; i++)
									fputc(' ',f);
								fseek(f,displ_next,SEEK_SET);
								break;
							}
						}
					}
				}
			}
		}
	}
	fclose(f);

	/* now save attachments which have been referenced */

	strcpy(buf,fname);
	p = strrchr(buf,'.');
	if(p != NULL)
		p[1] = 0;    /* drop the leafname */

	for(ix=0; ix<sizeof(attachments_used); ix++)
	{
		if(attachments_used[ix])
		{
			sprintf(ref,"%scid%02d",buf,ix);
			attach_get_start(t,ix);
			attach_saver(ref,(void *)t);
		}
	}
}   /* end of attach_open_html */




static void attach_start_save(wimp_eventstr *e, TEXTR *t, int a, int action)
/**************************************************************************/
{
	char *leaf_name;
	wimp_msgstr msg;
	char buf[128];

	attach_get_start(t,a);

	if(action == 1)
	{
		dbox_getfield(dbox_save_attach,2,buf,sizeof(buf));
		leaf_name = make_legal_fname(buf);
	}
	else
	{
		leaf_name = make_legal_fname(t->attach[a].name);
	}

	if(action < 2)
	{
		xfersend(t->attach[a].ftype,leaf_name,attach_length,attach_saver,attach_sender,0,e,(void *)t);
	}
	else
	{
		if(temp_fname[0] != 0)
			remove(temp_fname);

		/* save to wimp$scrap then open file */
		strcpy(temp_fname,get_temp_fname());
		attach_saver(temp_fname,(void *)t);

		if(t->attach[a].ftype == FILETYPE_HTML)
		{
			attach_html_related(t,a,temp_fname);
		}

		msg.data.dataopen.w = t->w_attach;
		msg.data.dataopen.i = a;
		msg.data.dataopen.x = 0;
		msg.data.dataopen.y = 0;
		msg.data.dataopen.size = attach_length;
		if(akbd_pollsh())
			msg.data.dataopen.type = 0xfff;
		else
			msg.data.dataopen.type = t->attach[a].ftype;
		strcpy(msg.data.dataopen.name,temp_fname);

		msg.hdr.size = (strlen(msg.data.dataopen.name) + 44 + 4) & ~3;
		msg.hdr.your_ref = 0;
		msg.hdr.action = wimp_MDATAOPEN;

		wimp_sendmessage(wimp_ESENDWANTACK,&msg,0);
	}

}   /* end of attach_start_save */




BOOL attachment_save(char *filename,TEXTR *t,int a_ix)
/***********************************************/
/* Called from export->attachments */
{
	attach_get_start(t,a_ix);
	return(attach_saver(filename,(void *)t));
}   /* end of attachment_save */



static BOOL attach_save_handler(dbox d, void *event, void *handle)
/****************************************************************/
{
	wimp_eventstr *e;
	TEXTR *t;

	e = (wimp_eventstr *)event;
	t = (TEXTR *)handle;

	/* look for mouse drag event */
	switch (e->e)
	{
	case wimp_EBUT:
		if(e->data.but.m.bbits & 0x50)
		{
			attach_start_save(e,t,attach_num,1);
			return(TRUE);
		}
		break;
	}

	return(help_handler(event,HELP_EXPORT));
}   /* end of attach_save_handler */






static void attach_menu_handler(int *hits)
/****************************************/
{
	TEXTR *t;
	int  a;
	int  size;
	int  diff;
	int  ix;
	int  length;
	int  n_chars;
	dbox d;
	int  ftype;
	wimp_icon *icon_ptr;
	char buf[200];
	os_regset regs;

	t = (TEXTR *)menu_handle;
	attach_num = a = dbox_menu_field;
	ftype = t->attach[a].ftype;

	switch(hits[0])
	{
	case 0:   /* save */
		if(hits[1] != 0)
			break;

		dbox_save_attach = dbox_new("SaveAs");
		dbox_raw_eventhandler(dbox_save_attach,attach_save_handler,(void *)t);

		sprintf(buf,"Sfile_%.3x",ftype);
		icon_ptr = dbox__fieldtoiconptr(dbox_save_attach,3);
		memcpy(icon_ptr->data.indirecttext.validstring,buf,9);

		dbox_setfield(dbox_save_attach,2,t->attach[a].name);
		dbox_show(dbox_save_attach);
		if(dbox_fillin(dbox_save_attach)==0)
		{
			dbox_getfield(dbox_save_attach,2,buf,sizeof(buf));
			attach_get_start(t,a);
			attach_saver(buf,t);
		}
		if(dbox_save_attach != NULL)
			dbox_dispose(&dbox_save_attach);
		menu_expected_persist = 1;
		break;

	case 1:   /* rename */
		ix = t->attach[a].name_displ;

		size = strlen(t->attach[a].name);

		menu_get_name[sizeof(t->attach[a].name)-1] = 0;

		length = strlen(menu_get_name);
		if((length == 0) || isspace(menu_get_name[0]))
		{
			beep();   /* zero length name */
			break;
		}
		diff = length - size;
		text_reset_header_order(t);

		if((ix > 0) && (ix != 0xff))
		{
			ix = t->attach[a].displ+ix+size;
			if(diff < 0)
			{
				move_up(t,ix,-diff);
			}
			else if(diff > 0)
			{
				if(move_down(t,ix,diff) != 0)
					break;
			}

			memcpy(&t->text_base[ix-size],menu_get_name,length);
			mark_changed(t);
		}

		strcpy(t->attach[a].name,menu_get_name);
		show_attachment_icons(t);

		menu_get_name[0] = 0;
		break;

	case 2:   /* remove */
		text_reset_header_order(t);

		a = dbox_menu_field+1;

		if(a < t->attachments)
		{
			/* there are further attachments after this deleted one */

			size = t->attach[a].displ - t->attach[a-1].displ;  /* size of deleted attachment */
			move_up(t,t->attach[a].displ,size);

			for(a=a; a<t->attachments; a++)
			{
				memcpy(&t->attach[a-1],&t->attach[a],sizeof(ATTACHMENT));
			}

		}
		else
		{
			t->text_length = t->attach[dbox_menu_field].displ;
		}
		t->attachments--;
		mark_changed(t);
		show_attachment_icons(t);

		/* reduce the text buffer size */
		size = t->text_length + TEXT_EXTRA;
		if(size < t->text_buf_size)
		{
			if(flex_extend((flex_ptr)&t->text_base,size)==1)
			{
				t->text_buf_size = size;
			}
		}
		break;

	case 3:  /* Info */
		if(hits[1] != 0)
			break;

		d = dbox_new("AttachInfo");

		regs.r[0] = 18;
		regs.r[2] = ftype;
		os_swix(0x29,&regs);      /* OS_FSControl 18  decode file type to text */
		regs.r[4] = 0;

		attach_set_sprite(t,a,ftype);
		sprintf(buf,"file_%.3x",ftype);
		icon_ptr = dbox__fieldtoiconptr(d,0);
		memcpy(icon_ptr->data.indirectsprite.name,buf,8);
		dbox_setfield(d,1,t->attach[a].name);

		sprintf(buf,"%s (%3x)",(char *)(&regs.r[2]),ftype);
		dbox_setfield(d,2,buf);

		/* find length of attachment */
		length = 0;
		attach_get_start(t,a);

		while(attach_start < attach_end)
		{
			if(uudecode_line(&t->text_base,&attach_start,&n_chars,attach_encoding)==NULL)
				break;
			length += n_chars;
		}

		dbox_setnumeric(d,3,length);
		dbox_setfield(d,4,encoding_names[t->attach[a].encoding]);
		dbox_show(d);
		dbox_fillin(d);
		dbox_dispose(&d);
		menu_expected_persist = 1;
		break;
	}
}   /* end of attach_menu_handler */



void attach_window_handler(wimp_eventstr *e, void *handle)
/******************************************************/
{
	int  bbits;
	TEXTR *t;
	int  filetype;
	char *filename;
	int  attachment;

	t = (TEXTR *)handle;

	switch (e->e)
	{
	case wimp_EOPEN:
		wimp_open_wind(&e->data.o);
		break;

	case wimp_ECLOSE:  /* Pass on close request */
		wimp_close_wind(e->data.o.w);
		break;

	case wimp_EBUT:
		bbits = e->data.but.m.bbits;
		attachment = e->data.but.m.i;
		if(bbits == 2)
		{
			if(e->data.but.m.i >= 0)
			{
				/* Menu click over an icon */
				dbox_menu_field = attachment;
				strcpy(menu_get_name,t->attach[attachment].name);  /* original name */
				menu_handle = handle;
				dbox_menu(wmenu_attach,attach_menu_handler,&e->data.but.m);
			}
		}
		if(bbits & 0x50)
		{
			/* drag */
			attach_start_save(e,t,attachment,0);
		}
		else if(bbits & 0x5)
		{
			/* double-click- open the file */
			attach_start_save(e,t,attachment,2);
		}
		else if(bbits & 0x500)
		{
#ifdef deleted
			if(bbits & 0x100)
			{
				/* adjust click */
				t->attach[attachment].selected ^= 1;
				wimp_set_icon_state(t->w_attach,attachment,t->attach[attachment].selected << 21,wimp_ISELECTED);
			}
			else
			{
				/* select click */
				if(t->attach[attachment].selected == 0)
				{
					for(i=0; i<t->attachments; i++)
					{
						if(i==attachment)
							x = 1;
						else
							x = 0;
						if(x != t->attach[i].selected)
							wimp_set_icon_state(t->w_attach,i,x<<21,wimp_ISELECTED);
						t->attach[i].selected = x;
					}
				}
			}
#endif
		}
		break;

	case wimp_EKEY:
		break;

	case wimp_ESCROLL:
		scroll_window(&e->data);
		break;

	case wimp_ESEND:
	case wimp_ESENDWANTACK:
		switch(e->data.msg.hdr.action)
		{
		case wimp_MDATASAVE:
			xferrecv_wimpscrap(e);
			break;

		case wimp_MDATALOAD:
			filetype = xferrecv_checkinsert(&filename);
			if(t->allow_edit)
				attach_create(t,filename,wimpscrap_check(filename),filetype);
			else
				beep();
			wimpscrap_check_clear();
			xferrecv_insertfileok();
			break;
		}
		break;

	default:   /* Ignore any other event */
		break;
	}
}   /* end of attach_window_handler */





/***********************************************************************/




static CARD **multipart_list;
static int multipart_total;
static dbox dbox_export;


void yenc_err(int part, char *msg, unsigned int x1,int x2)
/***********************************************/
{
	if(f_errlog == NULL)
	{
		f_errlog = fopen(fname_errlog,"w");
	}

	if(f_errlog==NULL)
		return;

	if(part>=0)
		fprintf(f_errlog,"yEnc part %2d:  ",part);

	fprintf(f_errlog,msg,x1,x2);
	fputc('\n',f_errlog);
}   /* end of yenc_err */



BOOL multipart_saver_proc(char *filename, void *handle)
/*****************************************************/
{
	CARD_EXPANDED *cardex;
	TEXTR *t;
	CARD *cptr;
	int  a_ix;   /* attachment number */
	int  failed_articles;
	int  filetype = 0xffd;
	char *p_out;
	FILE *f_out;
	FOLDREC *fr;
	int  n_chars;
	int  i;
	int  c;
	int  total=0;
	int  part_total;
	unsigned long  crc_val2;
	int  partno=0;
	int  yenc_part_size;
	char *msg;
	int missing;

	CARD_EXPANDED card_expanded;


	if(check_lock_lists(0xffff) != 0)
		return(FALSE);

	f_errlog = NULL;

	fr = (FOLDREC *)fr;
	t = &text_rec_temp;
	t->fr = fr;
	cardex = &card_expanded;

	f_out = fopen_werr(filename,"w","Attachment ");
	if(f_out == NULL)
	{
		return(FALSE);
	}

	strncpy0(fname_export,filename,sizeof(fname_export));
	i = strlen(fname_export);
	while(--i>0)
	{
		if(fname_export[i] == '.')
		{
			fname_export[i] = 0;
			break;
		}
	}

	set_lock_lists(4,fr);
	visdelay2_begin();

	failed_articles = 0;

	for(a_ix=1; a_ix <= multipart_total; a_ix++)
	{
		cptr = multipart_list[a_ix];
		if(cptr == NULL)
			continue;

		unpack_card(cptr, cardex);
		if(article_read(t,cardex->addr,cardex->docbox,cardex->text_offset,cardex->text_length,0,0,failed_articles) < 0)
		{
			failed_articles++;
			continue;
		}

		text_interpret_article(t,cardex,0);
		if(t->attachments > 0)
		{
			attach_get_start(t,t->attachments-1);

			if(a_ix==1)
			{
				filetype = t->attach[t->attachments-1].ftype;
			}
		}
		else
		{
			attach_start = t->internet_header;
			attach_end = t->text_length;
			attach_encoding = ENCODE_UUE;

			/* skip blank lines at start of data */
			while(((c = t->text_base[attach_start]) == '\n') || (c == '\r'))
				attach_start++;
		}

		part_total = 0;
		CrcInit();
		while(attach_start < attach_end)
		{
			p_out = uudecode_line(&t->text_base,&attach_start,&n_chars,attach_encoding);
			if(p_out==NULL)
				break;
			if(fwrite_werr(p_out,1,n_chars,f_out) < 0)
				break;
			part_total += n_chars;


			if(attach_encoding == ENCODE_YENC)
			{
				/* crc checks */
				for(i=0; i<n_chars; i++)
					CrcAdd(p_out[i]);
			}
			else
			{
				if(t->text_base[attach_start]=='_')
					break;

				while((attach_start < attach_end) && (t->text_base[attach_start] <= ' '))
				{
					attach_start++;
				}
			}
		}

		total += part_total;

		if(attach_encoding == ENCODE_YENC)
		{
			partno++;

			while(partno < yenc_part)
			{
				yenc_err(partno,"missing",0,0);
				partno++;
			}

			msg = NULL;
			if((yenc_part_size = (yenc_end-yenc_begin+1)) > 0)
			{
				missing = yenc_part_size - part_total;
				if(missing > 0)
					msg = "%d bytes missing data. Size should be %d";
				else if(missing < 0)
				{
					msg = "%d bytes excess data. Size should be %d";
					missing = -missing;
				}

				if(msg != NULL)
				{
					yenc_err(a_ix,msg,missing,yenc_part_size);
				}
			}

			if((yenc_pcrc != 0) && (msg == NULL))
			{
				crc_val2 = crc_val ^ 0xFFFFFFFFl;
				if(yenc_pcrc != crc_val2)
				{
					yenc_err(a_ix,"CRC error %08lx should be %08lx",(unsigned int)crc_val2,yenc_pcrc);
				}
			}
		}

		call_event_process(0);
	}


	fclose(f_out);
	set_filetype(filename,filetype);
	clear_lock_lists(4);

	if(partno < yenc_total)
	{
		if(yenc_total == (partno+1))
			yenc_err(partno+1,"missing",0,0);
		else
			yenc_err(-1,"Parts %2d to %2d are missing",partno+1,yenc_total);
	}

	if(total < yenc_size)
	{
		yenc_err(-1,"Missing data. Total size %d should be %d",total,yenc_size);
	}

	if(f_errlog != NULL)
	{
		fclose(f_errlog);
		dataopen(fname_errlog,0xfff);
	}

	if(failed_articles > 0)
	{
		werr(0,"Failed to read %d articles",failed_articles);
	}

	if(dbox_export != NULL)
	{
		dbox_dispose(&dbox_export);
		dbox_export = NULL;
	}

	if(t->text_base != NULL)
	{
		flex_free((flex_ptr)&t->text_base);
		t->text_base = NULL;
	}
	t->text_buf_size = 0;

	visdelay2_end();

	return(TRUE);
}   /* end of multipart_saver_proc */




static BOOL multipart_saver_proc2(char *filename, void *handle)
/*************************************************************/
{
	wimp_mousestr mousestr;

	/* check that we're not dragging onto the dbox window */
	wimp_get_point_info(&mousestr);
	if(mousestr.w == dbox_syshandle(dbox_export))
		return(FALSE);

	return(multipart_saver_proc(filename,handle));
}   /* end of multipart_saver_proc2 */






static void start_export_multipart(dbox dbox_export, wimp_eventstr *e, FOLDREC *fr)
/************************************************************************/
{
	int  length = -1;
	char fname[256];

	dbox_getfield(dbox_export,2,fname,sizeof(fname));


	if(e == NULL)
		multipart_saver_proc(fname,(void *)fr);
	else
		xfersend(0xfff,fname,length,multipart_saver_proc2,0,0,e,(void *)fr);

}   /* end of start_export_multipart */




static BOOL export_multipart_window_handler(dbox d, void *event, void *handle)
/***********************************************************/
{
	wimp_eventstr *e;
	FOLDREC *fr;

	e = (wimp_eventstr *)event;
	fr = (FOLDREC *)handle;

	/* look for mouse drag event */
	switch (e->e)
	{
	case wimp_EBUT:
		if(e->data.but.m.bbits & 0x50)
		{
			start_export_multipart(d,e,fr);
			return(TRUE);
		}
		break;
	}

	return(help_handler(event,HELP_EXPORT));
}   /* end of export_multipart_window_handler */




void list_export_multipart(FOLDREC *fr, int from_menu)
/***********************************************/
/* Export a multi-message attachment */
{
	CARD *cptr;
	int  ix;
	int  total = -1;
	int  n1, n2;
	char *p;
	char *p1;
	char *p2;
	char *p_tot;
	int  c;
	int  found;
	int  field;
	int  skip_missing = 0;
	char *fname = "Multipart";
	TEXTR *t;
	wimp_icon *icon_ptr;
	CARD_EXPANDED *cardex;
	CARD_EXPANDED card_expanded;

	CARD *parts[MAX_MULTIPART];
	int  date[MAX_MULTIPART];
	char buf[256];
	char *fname1;

	if(check_lock_lists(0xffff) != 0)
		return;

	memset(parts,0,sizeof(parts));

	list_enumerate_start(fr);

	while((cptr = list_enumerate_next(fr,fr->ixlist)) != NULL)
	{
		found = 0;

		if((cptr->status & STATUS_BIT_HDR_ONLY) &&
				((cptr->status & STATUS_MASK2) != STATUS_DRAFT))
			continue;

		p = &cptr->data[cptr->d_title];
		p1 = NULL;

		p2 = &p[strlen(p)];
		while(--p2 >= p)
		{
			if(((*p2 == ')') || (*p2 == ']')) && (isdigit(p2[-1]) || isdigit(p2[-2])))
			{
				c = *p2;
				if(c==')')
					c = '(';
				else
					c = '[';

				p1 = p2 - 10;   /* max length is [ 123/ 123] */
				if(p1 < p) p1 = p;
				p1 = strchr(p1,c);
				if(p1 != NULL)
					found = sscanf(p1+1,"%d/%d",&n1,&n2);
				break;
			}
		}

		if(found != 2)
		{
			/* haven't found [123/123] or (123/123), look for 123 of 123 */
			for(p1=&p[strlen(p)-6]; p1>=p; p1--)
			{
				if(isdigit(*p1) && (isspace(p1[-1])))
				{
					if((found = sscanf(p1,"%d of %d",&n1,&n2)) == 2)
						break;
				}
			}
		}

		if(found != 2)
			continue;

		if(total < 0)
		{
			total = n2;
			p_tot = p1;
		}
		else
		{
			if(n2 != total)
			{
				werr(0,"Inconsistent series totals %s and %s",p_tot,p1);
				break;
			}
		}
		if(n1 > total)
		{
			werr(0,"Number greater than total: %s",p1);
			break;
		}
		if(n1 >= MAX_MULTIPART)
		{
			werr(0,"More than %d parts",MAX_MULTIPART-1);
			break;
		}

		if(n1 < MAX_MULTIPART)
		{
			if(parts[n1] != NULL)
			{
				/* already a part of this number. is this one earlier? */
				if((cptr->date_box & DATE_MASK) >= date[n1])
					continue;  /* no, keep the previous */
			}
			parts[n1] = cptr;
			date[n1] = cptr->date_box & DATE_MASK;
		}
	}
	if(total < 0)
	{
		werr(0,"No parts found");
		return;
	}
	for(ix=1; ix<=total; ix++)
	{
		if((parts[ix]==NULL) && (skip_missing == 0))
		{
			sprintf(buf,"Part %d of %d missing, continue?",ix,total);
			switch(query3(buf,"Continue","Yes to all","Stop"))
			{
			case 1:
				break;
			case 0:
				skip_missing = 1;
				break;
			case 2:
				return;
			}
		}
	}

	ix = (total+1) * sizeof(void *);
	multipart_list = realloc(multipart_list,ix);
	multipart_total = total;
	memcpy(multipart_list,parts,ix);

	dbox_export = dbox_new("SaveAs");

	/* find name and filetype of 1st attachment */
	t = &text_rec_temp;
	t->fr = fr;

	if(multipart_list[1] != NULL)
	{
		visdelay2_begin();

		cardex = &card_expanded;
		unpack_card(multipart_list[1], cardex);
		if(article_read(t,cardex->addr,cardex->docbox,cardex->text_offset,cardex->text_length,0,0,0) >= 0)
		{
			text_interpret_article(t,cardex,0);
			if(t->attachments > 0)
			{
				fname = t->attach[0].name;
				sprintf(buf,"file_%.3x",t->attach[0].ftype);
				icon_ptr = dbox__fieldtoiconptr(dbox_export,3);
				memcpy(icon_ptr->data.indirectsprite.name,buf,8);
			}
		}
		visdelay2_end();
	}

	fname1 = make_legal_fname(fname);
	if(fname_export[0] != 0)
	{
		sprintf(buf,"%s.%s",fname_export,fname1);
		dbox_setfield(dbox_export,2,buf);
	}
	else
	{
		dbox_setfield(dbox_export,2,fname1);
	}

	dbox_show(dbox_export);
	if(!from_menu)
		set_dbox_at_pointer(dbox_export);

	dbox_raw_eventhandler(dbox_export,export_multipart_window_handler,(void *)fr);

	field = dbox_fillin(dbox_export);

	if(field == 0)
	{
		start_export_multipart(dbox_export,NULL,fr);
	}

	if(dbox_export != NULL)
	{
		dbox_dispose(&dbox_export);
		dbox_export = NULL;
	}

}   /* end of list_export_multipart */





/********************************************************************/




static void create_attachment_icons(TEXTR *t, int start)
/******************************************************/
/* Place the attachment icons in the attachments window,
   and set its extent */
{
	int  ix;
	int row = 0;
	int column = 0;
	wimp_icreate icon;
	wimp_i handle;

	icon.w = t->w_attach;
	icon.i.flags = 0x1700a10b;    /* bits 23 and 7 set to delete icon */
	/* button type 10, double-click-drag */


	for(ix=0; ix < start; ix++)
	{
		handle = ix;
		wimp_delete_icon(t->w_attach,handle);
	}

	for(ix=0; ix < t->num_attach; ix++)
	{
		icon.i.box.x0 = column * 240 + 8;
		icon.i.box.x1 = icon.i.box.x0 + 240;
		icon.i.box.y1 = -(row * 0x74 + 8);
		icon.i.box.y0 = icon.i.box.y1 - 0x6c;

		icon.i.data.indirecttext.buffer = t->attach[ix].name;
		icon.i.data.indirecttext.validstring = t->attach[ix].sprite;
		icon.i.data.indirecttext.bufflen = 12;

		wimp_create_icon(&icon,&handle);

		column++;
		if(column >= 5)
		{
			column = 0;
			row++;
		}
	}
}   /* end of create_attachment_icons */





void attachments_extend(TEXTR *t, int increase)
/*********************************************/
/* Increase the number of attachments allowed */
{
	int  previous;
	ATTACHMENT *a;

	previous = t->num_attach;

	if(increase < N_ATTACHMENTS)
		increase = N_ATTACHMENTS;

	a = calloc(sizeof(ATTACHMENT),previous + increase);
	if(a == NULL)
		return;

	t->num_attach = previous + increase;

	if(t->attach != NULL)
	{
		memcpy(a,t->attach,sizeof(ATTACHMENT)*previous);
		free(t->attach);
	}
	t->attach = a;
	create_attachment_icons(t,previous);

}   /* end of attachments_extend */





int get_ftype_from_extension(char *name)
/**************************************/
/* Look for .xxx extension at the end of the name */
{
	char *p1;
	os_regset regs;
	os_error *error;

	MNEMTAB *m;


	p1 = strrchr(name,'.');
	if((p1 == NULL) || (strlen(p1) > 5))
		return(-1);

	regs.r[0] = 3;
	regs.r[1] = (int)(p1);
	regs.r[2] = 0;
	error = os_swix(0x50b00,&regs);  /* MimeMap_Translate */

	if(error==NULL)
		return(regs.r[3]);

	for(m=&ftype_names[0]; m->name != NULL; m++)
	{
		if(strcmp_lc(p1,m->name)==0)
			break;
	}
	return(m->value);
}   /* end of get_ftype_from_extension */



char *get_extension_from_ftype(int ftype)
/***************************************/
{
	os_regset regs;
	os_error *error;

	MNEMTAB *m;
	static char buf[16];

	regs.r[0] = 0;
	regs.r[1] = ftype;
	regs.r[2] = 3;
	regs.r[3] = (int)buf;

	error = os_swix(0x50b00,&regs);   /* MimeMap_Translate */
	if(error == NULL)
		return((char *)regs.r[3]);


	for(m=&ftype_names[0]; m->name != NULL; m++)
	{
		if(m->value == ftype)
			break;
	}
	return(m->name);
}   /* end of get_extension_from_ftype */




typedef struct {
	char *mime;
	short ftype;
} MIME_MAP;

MIME_MAP mime_map[] = {
	"text/plain", 0xfff,
	"text/html",  0xfaf,

	"image/gif",  0x695,
	"image/jpeg", 0xc85,
	"image/tiff", 0xff0,
	"image/png",  0xb60,
	"image/bmp",  0x69c,

	"video/mpeg",      0xbf8,

	"audio/basic",     0xbd6,
	"audio/midi",      0xfd4,
	"audio/avi",       0xfb2,

	"application/postscript", 0xff5,
	"application/pdf", 0xadf,
	"application/octet-stream", 0xffd,
	"application/octet-stream", 0xfe4,   /* DOS file */
	"application/zip", 0xddc,
	"application/msword", 0xae6,
	"application/riscos", 0x7fff,
	"application/x-ms-bmp", 0x69c,

	"message/rfc822",    0xfff,
	NULL,0,
};


int text_mime_content_type(TEXTR *t,int a, int displ, int end)
/************************************************************/
{
	char *p;
	int  i;
	char *p1;
	char *p_end;
	char *name;
	int  name_length;
	int  ftype;
	int  x;
	MIME_MAP *m;
	char mime_type[60];
	char buf[256];
	os_regset regs;
	os_error *error;

	p = &t->text_base[displ];
	for(i=0; i<(sizeof(mime_type)-1); i++)
	{
		mime_type[i] = p[i];
		if((p[i] <= ' ') || (p[i] == ';'))
			break;
	}
	mime_type[i] = 0;

	if(memcmp_lc(mime_type,"multipart/",10)==0)
	{
		return(-2);    /* don't treat "multipart" as a separate attachment */
	}

	regs.r[0] = 2;
	regs.r[1] = (int)mime_type;
	regs.r[2] = 0;
	regs.r[3] = ftype;

	error = os_swix(0x50b00,&regs);  /* MimeMap_Translate */

	if(error)
	{
		ftype = -1;
		m = mime_map;
		while(m->mime != NULL)
		{
			if(strcmp_lc(mime_type,m->mime)==0)
			{
				ftype = m->ftype;
				break;
			}
			m++;
		}

		if(ftype == -1)
		{
			ftype = 0xffd;

			if((mimemap_err_flag==0) && (error->errnum == 486))
			{
				mimemap_err_flag = 1;   /* only show this error once */
				werr(0,mimemap_err,mime_type);
			}
		}
	}
	else
	{
		ftype = regs.r[3];
	}


	for(;;)
	{
		p = strpbrk(p,";\n");
		if(p >= &t->text_base[end])
			break;

		while(isspace(*(++p)));

		if(memcmp_lc(p,"name=",5)==0)
		{
			p += 5;
			while(isspace(*p)) p++;
			if(*p == '"')
			{
				p++;
			}
			p_end = strpbrk(p,"\";\n");   /* WRONG, should only look for " if started with " */
			/* but Voyager gets it wrong */

			name_length = p_end - p;

			memcpy(buf,p,name_length);
			buf[name_length]=0;
			name = buf;
			decode_iso8859(name,0);

			t->attach[a].name_displ = p - &t->text_base[t->attach[a].displ];

			if(strcmp_lc(mime_type,app_riscos)==0)
			{
				/* RISCOS application, get file type from end of filename */
				if(name[name_length-4] == ',')
				{
					ftype = read_hex(&buf[name_length-3]);
					name_length -= 4;
				}
				else
				{
					ftype = 0xffd;
				}
				if(name_length > 10)
				{
					p1 = strrchr(name,'.');
					if(p1 != NULL)
					{
						name_length -= (p1 + 1 - name);
						name = p1 + 1;
					}
				}
			}
			else if(ftype == 0xffd)
			{
				/*            if(strcmp(mime_type,"application/applefile") != 0)  */
				{
					if((x = get_ftype_from_extension(name)) >= 0)
						ftype = x;
				}
			}

			if(name_length >= sizeof(t->attach[0].name))
				name_length = sizeof(t->attach[0].name)-1;
			name[name_length] = 0;
			strcpy(t->attach[a].name,name);
		}
	}

	return(ftype);
}   /* end of text_mime_content_type */




static char *ftype_to_mime(int ftype)
/***********************************/
{
	MIME_MAP *m;
	os_regset regs;
	os_error *error;
	static char buf[40];

	regs.r[0] = 0;
	regs.r[1] = ftype;
	regs.r[2] = 2;
	regs.r[3] = (int)buf;
	error = os_swix(0x50b00,&regs);   /* MimeMap_Translate */

	if(error)
	{
		for(m=mime_map; m->mime != NULL; m++)
		{
			if(m->ftype == ftype)
				break;
		}

		if(m->mime == NULL)
		{
			if((mimemap_err_flag==0) && (error->errnum == 486))
			{
				sprintf(buf,"&%.3x",ftype);
				werr(0,mimemap_err,buf);
				mimemap_err_flag = 1;  /* only show this error once */
			}

			buf[0] = 0;
		}
		else
		{
			strcpy(buf,m->mime);
		}
	}

	if(strcmp_lc(buf,app_riscos)==0)
		buf[0]=0;   /* meaning 'not found' */
	return(buf);
}   /* end of ftype_to_mime */




void attach_create(TEXTR *t, char *fname, char *attach_name, int ftype)
/***************************************************************/
/* A file has been dropped onto the text or edit-window button bar */
{
	int  i;
	int  c;
	int  length;
	FILE *f;
	char *p;
	int  ix;
	int  file_length;
	int  len;
	int  offset;
	char *dot;
	char *p1;
	int  ext_len;
	int  encoding_type;
	char *mime_type="";
	char fname2[64];
	char buf[256];


	if(ftype > 0xfff)
	{
		werr(0,"Can't use directories as attachments.  Enclose it within an archive.");
		return;
	}
	file_length = get_filelength(fname);
	if(file_length == 0)
	{
		werr(0,"Zero length file");
	}

	f = fopen_werr(fname,"r","");
	if(f == NULL)
	{
		return;
	}

	if(t->attachments >= t->num_attach)
	{
		/* need to allocate more attachment data */
		attachments_extend(t,1);

		if(t->attachments >= t->num_attach)
		{
			/* extension must have failed */
			fclose(f);
			return;
		}
	}

	visdelay2_begin();

	encoding_type = ENCODE_UUE;
	if(t->boundary_string_len > 0)
	{
		encoding_type = ENCODE_BASE64;  /* we already have a MIME attachment */
	}
	else if(t->text_type == X_MAIL)
	{
		if(t->use_mime)
			encoding_type = ENCODE_BASE64;  /* or _BASE64 for mime */
	}


	switch(encoding_type)
	{
	case ENCODE_UUE:
		length = uuencode_length(file_length);
		break;

	case ENCODE_BASE64:
		if(t->boundary_string_len == 0)
			make_boundary_string(t);

		mime_type = ftype_to_mime(ftype);


		if(memcmp_lc(mime_type,"text/",5)==0)
		{
			/* try it as quoted-printable and see how long it is */
			i = encode_examine_file(file_length,f);
			rewind(f);

			encoding_type = 0;
			if(i & 4)
				encoding_type = ENCODE_BASE64;
			else if(i != 0)
			{
				if(options.mail_8bit==0)
					encoding_type = ENCODE_QUOTED;
				else if(i & 2)
					encoding_type = ENCODE_8BIT;
			}
		}

		if(encoding_type == ENCODE_BASE64)
		{
			length = b64encode_length(file_length);
		}
		else if(encoding_type == ENCODE_QUOTED)
		{
			length = quote_encode_file(file_length,f,NULL,0);
			rewind(f);
		}
		else
			length = file_length;

		length += (t->boundary_string_len + 128);
		break;
	}
	length += (t->text_length + TEXT_EXTRA);

	/* extend the text area */
	pointer_sanity_check(3,t->text_base);

	if(flex_extend((flex_ptr)&t->text_base,length)==0)
	{
		visdelay2_end();
		malloc_err(30+0x200);
		fclose(f);
		return;
	}

	t->text_buf_size = length;

	ix = t->attachments;
	t->attach[ix].ftype = ftype;

	/* find last part of filename */
	p = &attach_name[strlen(attach_name)];
	while((p >= attach_name) && (*p != '.'))
		p--;
	p++;
	strcpy(fname2,p);
	p = fname2 + strlen(fname2);
	dot = p1 = p;
	while(--p > fname2)
	{
		if(*p == '/')
		{
			/* replace all / by . and find position of last / */
			*p = '.';
			if(dot==p1)
				dot = p;
		}
	}
	if(t->use_mime == 2)
	{
		/* add filetype suffix */
		ext_len = p1 - dot - 1;
		if(ext_len == 0)
			*dot = 0;    /* trailing . */

		p = get_extension_from_ftype(ftype);
		if(p == NULL)
		{
			if((mime_type[0]==0) && (ext_len <= 0))
				werr(0,"Warning: Filetype &%.3X is not recognised in MIME/PC mode. Filetype information will not be transmitted",ftype);
		}
		else
		{
			if(strcmp(p,"html")==0)
				p = "htm";
			else if(strcmp(p,"jpeg")==0)
				p = "jpg";
			else if(strcmp(p,"tiff")==0)
				p = "tif";

			i = strlen(p);
			if((ext_len < 3) && (i > ext_len))
			{
				/* current extension may have been truncated, replace it */
				*dot = 0;
				sprintf(fname2,"%s.%s",fname2,p);
			}
		}
	}

	t->attach[ix].displ = t->text_length;
	t->attach[ix].encoding = encoding_type;

	if(encoding_type == ENCODE_UUE)
	{
		sprintf(buf,"\nbegin 644 %s\n",fname2);
		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}
		t->attach[ix].name_displ = 11;
	}
	else
	{
		if((mime_type[0]==0) && (t->use_mime==2))
		{
			/* PC-MIME, use default of application/octet-stream */
			strcpy(mime_type,"application/octet-stream");
		}

		if(mime_type[0]==0)
		{
			sprintf(buf,"\n--%s\nContent-Type: application/riscos; Name=\"%s,%.3X",t->boundary_string,fname2,ftype);
			offset = 4;
		}
		else
		{
			sprintf(buf,"\n--%s\nContent-Type: %s; Name=\"%s",t->boundary_string,mime_type,fname2);
			offset = 0;
		}

		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}
		t->attach[ix].name_displ = i-strlen(fname2)-offset;
	}


	switch(encoding_type)
	{
	case ENCODE_UUE:
		t->text_length = uuencode_file(file_length, f, &t->text_base, t->text_length);
		sprintf(buf,"RiscOS filetype : %.3X\n\n",ftype);
		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}
		break;

	case ENCODE_QUOTED:
		sprintf(buf,"\"\n%s%s\n",content_encoding,content_quoted_printable);
		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}

		t->text_length = quote_encode_file(file_length,f,&t->text_base,t->text_length);
		break;

	case ENCODE_BASE64:
		sprintf(buf,"\"\n%s%s\n",content_encoding,content_encoding_base64);
		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}

		t->text_length = b64encode_file(file_length,f,&t->text_base, t->text_length);
		break;

	case ENCODE_8BIT:
		sprintf(buf,"\"\n%s%s\n",content_encoding,content_encoding_8bit);
		len = strlen(buf);
		for(i=0; i<len; i++)
		{
			t->text_base[t->text_length++] = buf[i];
		}

		for(i=0; i<file_length; i++)
		{
			c = fgetc(f);
			t->text_base[t->text_length++] = c;
		}
		break;

	default:
		sprintf(buf,"\"\n\n");
		memcpy(&t->text_base[t->text_length],buf,3);
		t->text_length += 3;

		for(i=0; i<file_length; i++)
		{
			c = fgetc(f);
			t->text_base[t->text_length++] = c;
		}
		break;
	}

	fclose(f);

	strcpy(t->attach[ix].name,fname2);
	t->attach[ix].length = t->text_length - t->attach[ix].displ;

	t->attachments++;

	calc_line_tab(t);   /* just in case it's got messed up somehow */
	mark_changed(t);
	show_attachment_icons(t);
	open_article(t,0x82);
	visdelay2_end();
}   /* end of attach_create */


