/*
    NewsCoaster
    Copyright (C) 1999-2002 Mark Harman

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    NewsCoaster Homepage :
        http://newscoaster.tripod.com/

    Mailing List to hear about announcements of new releases etc at:
        http://groups.yahoo.com/group/newscoaster-announce/
*/

#include <libraries/mui.h>

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

#include <proto/dos.h>

#include "Vector.h"
#include "various.h"
#include "misc.h"
#include "DateHandler.h"
#include "strings.h"
#include "main.h"

/* Variable for the nLog #define. */
BOOL logbool = FALSE;

/* The log file handle. */
BPTR logFile=NULL;

/* Reads a line (either CR or LF terminated) from 'file' into 'buffer'.
 */
void Readln(BPTR file,char * buffer) {
	LONG ch = 0;
	for(;;) {
		ch=FGetC(file);
		if(ch==-1 || ch==10 || ch==13) {
			*buffer=0;
			break;
		}
		*buffer=(char)ch;
		buffer++;
	}
}

/* Decodes quoted printable plain text from 'in' and copies the result to
 * 'out'.
 * 'length' is the length of the encoded text.
 * 'nl' should be TRUE iff newline characters are to be skipped (this will
 * normally be the case).
 */
int decode_qprint(char * in,char * out,int length,BOOL nl) {
	// leave as %d not %s below!
	nLog("decode_qprint((char *)%d,(char *)%d,(int)%d,(BOOL)%d) called\n",in,out,length,nl);
	char t[4]="";
	int asc=0;
	int d=0;
	char * end=&in[length];
	do {
		if(*in=='=') {
			in++;
			if(*in==10) {
				if(nl) {
					in++;
					if(*in==13)
						in++;
				}
			}
			else {
				strncpy(t,in,2);
				t[2]=0;
				in+=2;
				sscanf(t,"%x",&asc);
				*out++=(unsigned char)asc;
				d++;
			}
		}
		else {
			*out++=*in++;
			d++;
		}
	} while(in<end);
	*out= '\0';
	return d;
}

/* Output a newline character to 'out', followed by 'quoteLevel' '>'
 * characters.
 */
void newLineHtml(char **out,int quoteLevel) {
	*(*out)++ = 10;
	for(int i=0;i<quoteLevel;i++) {
		*(*out)++ = '>';
		*(*out)++ = ' ';
	}
}

/* Converts HTML from 'in' into plain text and outputs the result to 'out'.
 * 'length' is the length of the HTML text.
 * 'reset' is whether to reset the quote level.
 */
void parse_html(char * in,char * out,int length,BOOL reset) {
	// leave as %d not %s below!
	nLog("parse_html((char *)%s,(char *)%d,(int)%d,(BOOL)%d) called\n",in,out,length,reset);
	static BOOL inTag=FALSE;
	static int quoteLevel = 0;
	if(reset) {
		inTag=FALSE;
		quoteLevel = 0;
	}
	char t[16] = "";
	char *end=&in[length];
	do {
		if(*in==10 || *in==13) {
			*out++ = ' ';
			in++;
		}
		else if(*in == '&') {
			in++;
			int i=0;
			while(*in != ';' && i<15)
				t[i++] = *in++;
			in++;
			t[i] = 0;
			if(iequals(t,"nbsp"))
				*out++ = ' ';
			else if(iequals(t,"amp"))
				*out++ = '&';
			else if(iequals(t,"copy"))
				*out++ = '©';
			else if(iequals(t,"lt"))
				*out++ = '<';
			else if(iequals(t,"gt"))
				*out++ = '>';
			else if(*t == '#' && i>1) {
				int val = atoi(&t[1]);
				*out++ = (char)val;
			}
		}
		else {
			if(inTag) {
				if(*in == '>')
					inTag=FALSE;
				in++;
			}
			else {
				if(*in == '<') {
					inTag=TRUE;
					in++;
					strncpy(t,in,15);
					t[15]=0;
					if(strincmp(in,"B>",2)==0)
						*out++ = '*';
					else  if(strincmp(in,"/B>",3)==0)
						*out++ = '*';
					else  if(strincmp(in,"I>",2)==0)
						*out++ = '/';
					else  if(strincmp(in,"/I>",3)==0)
						*out++ = '/';
					else  if(strincmp(in,"U>",2)==0)
						*out++ = '_';
					else  if(strincmp(in,"/O>",3)==0)
						*out++ = '_';
					else  if(strincmp(in,"BR>",3)==0)
						newLineHtml(&out,quoteLevel);
					else  if(strincmp(in,"P>",2)==0) {
						newLineHtml(&out,quoteLevel);
						newLineHtml(&out,quoteLevel);
					}
					else if(strincmp(in,"BLOCKQUOTE>",11)==0 || strincmp(in,"BLOCKQUOTE ",11)==0) {
						quoteLevel++;
						newLineHtml(&out,quoteLevel);
					}
					else if(strincmp(in,"/BLOCKQUOTE>",12)==0) {
						if(quoteLevel>0) {
							quoteLevel--;
							newLineHtml(&out,quoteLevel);
						}
					}
				}
				else
					*out++ = *in++;
			}
		}
	} while(in<end);
	*out=0;
}

/* Decodes base64 'in' and copies the result to 'out'.
 * 'length' is the length of the encoded text.
 */
int decode_base64(char *in,char *out,int length) {
	// leave as %d not %s below!
	nLog("decode_base64((char *)%d,(char *)%d,(int)%d) called\n",in,out,length);
	char *buffer = new char[length + 1024];
	char *buffero = buffer;
	int d=0,d2=0,c=0;
	char *end = &in[length];
	do {
		if(*in>='A' && *in<='Z') {
			*buffer++=*in - 'A';
			d++;
		}
		else if(*in>='a' && *in<='z') {
			*buffer++=*in - 'a' + 26;
			d++;
		}
		else if(*in>='0' && *in<='9') {
			*buffer++=*in - '0' + 52;
			d++;
		}
		else if (*in=='+') {
			*buffer++=62;
			d++;
		}
		else if (*in=='/') {
			*buffer++=63;
			d++;
		}
		else if (*in=='=') {
			//end of data?
			break;
		}
		in++;
	} while(in<end);
	*buffer = '\0';
	d2=0;
	//printf("d = %d\n",d);
	for(c=0;c<d;c+=4) {
		if( c + 3 >= d ) {
			// needs special handling!
			*out++ = buffero[c]*4 + (buffero[c+1] / 16);
			d2++;

			if( c + 2 >= d ) {
				//printf("!\n");
				break;
			}

			*out++ = (buffero[c+1] % 16)*16 + (buffero[c+2] / 4);
			d2++;
			break;
		}
		*out++ = buffero[c]*4 + (buffero[c+1] / 16);
		*out++ = (buffero[c+1] % 16)*16 + (buffero[c+2] / 4);
		*out++ = (buffero[c+2] % 4)*64 + buffero[c+3];
		d2+=3;
	}
	delete [] buffero;
	//printf("d2 = %d\n",d2);
	return d2;
}

int encode_base64(char *in,char *out,int length) {
	// leave as %d not %s below!
	nLog("encode_base64((char *)%d,(char *)%d,(int)%d) called\n",in,out,length);
	char *buffer = new char[(length*5)/3+8192];
	char *buffero = buffer;
	int d=0,d2=0,c=0;
	//printf("length = %d\n",length);
	for(c=0;c<length;c+=3) {
		if( c + 2 >= length ) {
			// needs special handling!
			*buffer++ = (in[c] & 252)/4;
			if( c + 1 >= length ) {
				*buffer++ = (in[c] & 3)*16;
				*buffer++=255;
				*buffer++=255;
				d+=4;
				break;
			}
			*buffer++ = (in[c] & 3)*16 + (in[c+1] & 240)/16;
			*buffer++ = (in[c+1] & 15)*4;
			*buffer++=255;
			d+=4;
			break;
		}
		*buffer++ = (in[c] & 252)/4;
		*buffer++ = (in[c] & 3)*16 + (in[c+1] & 240)/16;
		*buffer++ = (in[c+1] & 15)*4 + (in[c+2] & 192)/64;
		*buffer++ = (in[c+2] & 63);
		d+=4;
	}
	for(c=0;c<d;c++) {
		unsigned char ch = (unsigned char)buffero[c];
		if(ch < 26) {
			*out++=buffero[c] + 'A';
			d2++;
		}
		else if(ch >= 26 && ch < 52) {
			*out++=buffero[c] - 26 + 'a';
			d2++;
		}
		else if(ch >= 52 && ch < 62) {
			*out++=buffero[c] - 52 + '0';
			d2++;
		}
		else if(ch == 62) {
			*out++='+';
			d2++;
		}
		else if(ch == 63) {
			*out++='/';
			d2++;
		}
		else if(ch == 255) {
			*out++='=';
			d2++;
		}
		if((d2+1) % 73 == 0) {
			*out++=10;
			d2++;
		}
	}
	*out = '\0';
	delete [] buffero;
	return d2;
}

// todo: better handling of multifile messages
int ydecode(BOOL *corrupt,char *in,char *out,int length,uuEncodeData *uudata) {
	nLog("ydecode((BOOL *)%d,(char *)%d,(char *)%d,(int)%d,(uuEncodeData *)%d) called\n",corrupt,in,out,length,uudata);
	*corrupt = FALSE;
	char *end=&in[length];
	char *in2=NULL,*begin=NULL;
	char filename[1024] = "unnamed";
	//char bbuf[1024] = "";
	//strcpy(filename_g,"unnamed");
	if(strincmp(in,"=ybegin ",8)==0) {
		in2 = in;
		begin = in;
	}
	else {
		in2 = stristr_q(in,"\n=YBEGIN ");
		begin = in2+1;
	}
	in2 = strchr((char *)&in2[1],'\n');
	in2++;

	int size1 = -1;
	{
		int size = (int)(in2-begin);
		char *temp = new char[size + 1];
		strncpy(temp,(char *)begin,size);
		temp[size]=0;
		// name
		char *ptr = stristr_q(temp,"NAME=");
		if(ptr != NULL) {
			ptr += 5;
			char *eptr = NULL;
			if(*ptr == '\"') {
				ptr++;
				eptr = strchr(ptr,'\"');
			}
			else {
				eptr = strchr(ptr,' ');
				char *nl = strchr(ptr,'\n');
				if( (nl != NULL && nl < eptr) || eptr == NULL )
					eptr = nl;
			}
			if(eptr == NULL)
				strcpy(filename,ptr);
			else {
				strncpy(filename,ptr,(int)(eptr - ptr));
				filename[(int)(eptr - ptr)] = '\0';
			}
			StripTrail(filename,'\r');
		}
		// size
		ptr = stristr_q(temp,"SIZE=");
		if(ptr != NULL) {
			size1 = atoi( &ptr[5] );
			nLog("  found size1 = %d\n",size1);
		}
		delete [] temp;
	}

	strncpy(uudata->filename,filename,UUENCODEDATA_FILENAME_LEN);
	uudata->filename[UUENCODEDATA_FILENAME_LEN] = '\0';
	char *dot = NULL,*ndot = NULL;
	dot = strchr(uudata->filename,'.');
	while(dot != NULL) {
		ndot = strchr(&dot[1],'.');
		if(ndot == NULL)
			break;
		dot = ndot;
	}
	if(dot)
		strncpy(uudata->ext,dot,UUENCODEDATA_EXT_LEN);
	else
		strncpy(uudata->ext,".txt",UUENCODEDATA_EXT_LEN);
	uudata->ext[UUENCODEDATA_EXT_LEN] = '\0';

	int d2 = 0;
	//int size2 = -1;
	do {
		unsigned char c = *in2++;
		if(c == '\0' || c == 10 || c == 13) {
		//if(c == '\0' || c == 9 || c == 10 || c == 13) {
			// skip
		}
		else if(c == '=') {
			char c2 = *in2++;
			if(c2 == 'y' || c2 == 'Y') {
				//printf("%c %c %c\n",in2[0],in2[1],in2[2]);
				/*if(strincmp_q(in2,"END ",4)==0) {
					char *ptr = stristr_q(in2,"SIZE=");
					if(ptr != NULL) {
						size2 = atoi( &ptr[5] );
						nLog("  found size2 = %d\n",size2);
					}
				}*/
				char *nl = strchr(in2,'\n');
				if(nl != NULL)
					in2 = &nl[1];
				//printf(":%d %d\n",in2,end);
			}
			else {
				c2 = (unsigned char)(c2 - 64);
				c2 = (unsigned char)(c2 - 42);
				*out++ = c2;
				d2++;
			}
		}
		else {
			c = (unsigned char)(c - 42);
			*out++ = c;
			d2++;
		}
	} while(in2<end);
	*out = '\0';

	nLog("  actual length is = %d\n",d2);
	//if(d2 != size1 || d2 != size2) {
	if(d2 != size1) {
		*corrupt = TRUE;
	}

	return d2;
}

int uudecode(char *in,char *out,int length,uuEncodeData *uudata) {
	// leave as %d not %s below!
	nLog("uudecode((char *)%d,(char *)%d,(int)%d,(uuEncodeData *)%d) called\n",in,out,length,uudata);
	char *buffer = new char[length + 1024];
	char *buffero = buffer;
	int d=0,d2=0,c=0;
	char *end=&in[length];
	char *in2=NULL,*begin=NULL;
	char filename[1024] = "";
	//char bbuf[1024] = "";
	//if(strincmp(in,"begin ",6)==0 && isdigit(in[6]) ) {
	// we should be at the start anyway
	if(strincmp(in,"begin ",6)==0) {
		in2=in;
		begin=in;
	}
	else {
		in2=stristr_q(in,"\nBEGIN ");
		begin=in2+1;
	}
	in2=strchr(&in2[1],'\n');
	in2++;
	{
		int size = (int)(in2-begin);
		char *temp = new char[size + 1];
		strncpy(temp,(char *)begin,size);
		temp[size] = '\0';
		word(filename,temp,3);
		delete [] temp;
	}
	strncpy(uudata->filename,filename,UUENCODEDATA_FILENAME_LEN);
	uudata->filename[UUENCODEDATA_FILENAME_LEN] = '\0';
	char *dot = NULL,*ndot = NULL;
	dot = strchr(uudata->filename,'.');
	while(dot != NULL) {
		ndot = strchr(&dot[1],'.');
		if(ndot == NULL)
			break;
		dot = ndot;
	}
	if(dot)
		strncpy(uudata->ext,dot,UUENCODEDATA_EXT_LEN);
	else
		strncpy(uudata->ext,".txt",UUENCODEDATA_EXT_LEN);
	uudata->ext[UUENCODEDATA_EXT_LEN] = '\0';
	int count = 0;
	int k = 0;
	do {
		if(*in2=='\n' || *in2=='\0')
			break;
		count = (int)*in2++ - 32;
		if(count % 3 != 0) {
			count+=3;
			if(count % 3 == 1)
				d-=2;
			if(count % 3 == 2)
				d--;
		}
		count = (count / 3) * 4;
		for(k=0;k<count;k++) {
			*buffer++ = (*in2++ - 32) % 64;
			d++;
		}
		while(*in2!='\n' && *in2!='\0')
			in2++;
		if(*in2=='\0')
			break; // just to be safe
		in2++;
	} while(in2<end);
	*buffer = '\0';
	d2=0;
	for(c=0;c<d;c+=4) {
		if( d-c<4) {
			// needs special handling!
			*out++ = buffero[c]*4 + (buffero[c+1] / 16);
			d2++;
			if(c+2>=d)
				break;
			*out++ = (buffero[c+1] % 16)*16 + (buffero[c+2] / 4);
			d2++;
			break;
 		}
		*out++ = buffero[c]*4 + (buffero[c+1] / 16);
		*out++ = (buffero[c+1] % 16)*16 + (buffero[c+2] / 4);
		*out++ = (buffero[c+2] % 4)*64 + buffero[c+3];
		d2+=3;
	}
	delete [] buffero;
	return d2;
}

/* Converts a file extension 'ext' into a MIMEType 'type'.
 */
void getMIMEType(char * type,char * ext) {
	// leave as %d not %s below!
	nLog("getMIMEType((char *)%s,(char *)ext) called\n",type,ext);
	if(iequals(ext,".txt"))
		strcpy(type,"text/plain");
	else if(iequals(ext,".html"))
		strcpy(type,"text/html");
	else if(iequals(ext,".htm"))
		strcpy(type,"text/html");
	else if(iequals(ext,".shtml"))
		strcpy(type,"text/html");
	else if(iequals(ext,".guide"))
		strcpy(type,"text/x-aguide");
	else if(iequals(ext,".exe"))
		strcpy(type,"application/octet-stream");
	else if(iequals(ext,".ps"))
		strcpy(type,"application/postscript");
	else if(iequals(ext,".lha"))
		strcpy(type,"application/x-lha");
	else if(iequals(ext,".lzx"))
		strcpy(type,"application/x-lzx");
	else if(iequals(ext,".zip"))
		strcpy(type,"application/x-zip");
	else if(iequals(ext,".jpeg"))
		strcpy(type,"image/jpeg");
	else if(iequals(ext,".jpg"))
		strcpy(type,"image/jpeg");
	else if(iequals(ext,".png"))
		strcpy(type,"image/png");
	else if(iequals(ext,".gif"))
		strcpy(type,"image/gif");
	else if(iequals(ext,".iff"))
		strcpy(type,"image/x-ilbm");
	else if(iequals(ext,".wav"))
		strcpy(type,"audio/x-wav");
	else if(iequals(ext,".mpeg"))
		strcpy(type,"video/mpeg");
	else if(iequals(ext,".mpg"))
		strcpy(type,"video/mpeg");
	else if(iequals(ext,".mov"))
		strcpy(type,"video/quicktime");
	else if(iequals(ext,".anim"))
		strcpy(type,"video/x-anim");
}

BOOL translateCharset(unsigned char * text,char * charset) {
	if(*charset==0) {
		BOOL bit7=TRUE;
		while(*text!=NULL) {
			/*if(*text == 224) // bizarre hack, this ascii code appears sometimes for some unknown reason!
				*text = 32;*/
			if(*text++ > 128)
				bit7=FALSE;
		}
		return bit7;
	}
	else {
		BOOL bit7=TRUE;
		while(*text!=NULL) {
			/*if(*text == 224) // bizarre hack, this ascii code appears sometimes for some unknown reason!
				*text = 32;*/
			if(*text++ > 128)
				bit7=FALSE;
		}
		return bit7;
	}
	
}

/* Logging function.
 */
/*void nLog(char * text,...) {
	if((account.flags & Account::LOGGING)==0)
		return;*/
BOOL LogFunc(char * text,...) {
	struct DateStamp ds;
	DateTime dt;
	char dateLog[32] = "";
	char timeLog[32] = "";
	const int LEN = 8192;
	static char message[LEN+1] = "";

	va_list vlist;
	if(logFile!=NULL && text!=NULL) {
		va_start(vlist,text);
		message[LEN] = '\0';
		vsprintf(message,text,vlist);
		if(message[LEN] != '\0') {
			// buffer overrun!
			char err[] = "Logging buffer overflow!\n";
			printf("%s",err);
			Write(logFile,err,strlen(err));
			message[LEN] = '\0';
		}
		va_end(vlist);
		DateStamp(&ds);
		dt.dat_Stamp=ds;
		dt.dat_Format=FORMAT_CDN;
		dt.dat_Flags=NULL;
		dt.dat_StrDay=NULL;
		dt.dat_StrDate=(unsigned char *)dateLog; // puts format dd-mm-yy
		dt.dat_StrTime=(unsigned char *)timeLog;
		DateToStr(&dt);
		Write(logFile,dateLog,strlen(dateLog));
		Write(logFile," - ",3);
		Write(logFile,timeLog,strlen(timeLog));
		Write(logFile," : ",3);
		Write(logFile,message,strlen(message));
		return TRUE;
	}
	return FALSE;
}

void run_test(int major,int minor) {
	//const verbose = TRUE;
	const verbose = FALSE;
	BOOL ok = FALSE;
	//printf(" Test %d.%d\n",major,minor);
	if(major == 0) {
		int i = 0;
		int length = 65536 + minor;
		char *original = new char[length];
		for(i=0;i<length;i++)
			original[i] = rand() % 256;

		char *encoded = new char[length * 2 + 256];
		int enc_length = encode_base64(original,encoded,length);
		char *decoded = new char[enc_length + 256];
		int dec_length = decode_base64(encoded,decoded,enc_length);
		if(verbose) {
			printf("Original Length = %d\n",length);
			printf("Encoded Length  = %d\n",enc_length);
			printf("Decoded Length  = %d\n",dec_length);
		}
		ok = TRUE;
		if( dec_length != length ) {
			if(verbose) {
				printf("Lengths disagree!\n");
			}
			ok = FALSE;
		}
		for(i=0;ok && i<length;i++) {
			if(original[i] != decoded[i]) {
				if(verbose) {
					printf("Disagreement %d versus %d at position %d\n",
						original[i],decoded[i],i);
				}
				ok = FALSE;
			}
		}
		delete [] original;
		delete [] encoded;
		delete [] decoded;
	}
	else if(major == 1) {
		char buffer[1024] = "";
		ok = TRUE;
		if(minor == 0)
			DateHandler::get_datenow(buffer,0,FALSE);
		else if(minor == 1)
			DateHandler::get_datenow(buffer,0,TRUE);
		else if(minor == 2) {
			char c_date[256] = "";
			char c_time[256] = "";
			struct DateStamp ds;
			struct DateStamp ds2;
			DateStamp(&ds);
			DateHandler::get_date(buffer,0,FALSE,ds);
			DateHandler::read_date(&ds2,buffer,c_date,c_time);
			ds.ds_Tick = 0;
			ds2.ds_Tick = 0;
			if(verbose) {
				printf("date: %s\n",buffer);
				printf("c_date: %s\n",c_date);
				printf("c_time: %s\n",c_time);
				printf("Compare: %d\n",CompareDates(&ds, &ds2));
				printf("ds: %d %d %d\n",ds.ds_Days,ds.ds_Minute,ds.ds_Tick);
				printf("ds: %d %d %d\n",ds2.ds_Days,ds2.ds_Minute,ds2.ds_Tick);
			}
			if( CompareDates(&ds, &ds2) != 0 ) {
				ok = FALSE;
			}
		}
	}
	else if(major == 2) {
		char text0[] = "This is some interesting piece of text";
		char text1[] = "This is some interesting piece of Text";
		char text2[] = "This is some text";
		char buffer[4096] = "";
		if(minor==0)
			ok = ( equals(text0,text0) == TRUE );
		else if(minor==1)
			ok = ( equals(text0,text1) == FALSE );
		else if(minor==2)
			ok = ( equals(text0,text2) == FALSE );
		else if(minor==3)
			ok = ( iequals(text0,text0) == TRUE );
		else if(minor==4)
			ok = ( iequals(text0,text1) == TRUE );
		else if(minor==5)
			ok = ( iequals(text0,text2) == FALSE );
		else if(minor==6) {
			wordFirst(buffer,text0);
			ok = ( equals(buffer,"This") == TRUE );
		}
		else if(minor==7) {
			int len = wordFirstAndLen(buffer,text0);
			ok = ( len == 4 && equals(buffer,"This") == TRUE );
		}
		else if(minor==8) {
			wordFirstUpper(buffer,text0);
			ok = ( iequals(buffer,"This") == TRUE );
		}
		else if(minor==9) {
			int len = wordFirstAndLenUpper(buffer,text0);
			ok = ( len == 4 && equals(buffer,"THIS") == TRUE );
		}
		else if(minor==10) {
			char *ptr = wordLast(text0);
			ok = ( equals(ptr,"text") == TRUE );
		}
		else if(minor==11) {
			word(buffer,text0,3);
			ok = ( equals(buffer,"some") == TRUE );
		}
		else if(minor==12) {
			translateIso(buffer,text0);
			ok = ( equals(buffer,text0) == TRUE );
		}
		else if(minor==13) {
			translateIso(buffer,"? =?ISO-8859-2?Q?Mark=20blah=20blah?= more text");
			ok = ( equals(buffer,"? Mark blah blah more text") == TRUE );
		}
		else if(minor==14) {
			translateIso(buffer,"? =?ISO-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= blah?");
			ok = ( equals(buffer,"? u understand the example. blah?") == TRUE );
		}
		else if(minor==15) {
			translateIso(buffer,"=?ISO-8859-2?B?Mark");
			ok = ( equals(buffer,"=?ISO-8859-2?B?Mark") == TRUE );
		}
	}
	else {
		printf("UNKNOWN TEST SUITE!\n");
	}
	if(ok)
		printf("===== PASSED");
	else
		printf("***** FAILED");
	printf(" %d . %d\n",major,minor);
}

void run_test_set(int major,int minor) {
	int n_tests = 0;
	if(major == 0) {
		// attachment encoding/decoding
		n_tests = 6;
	}
	else if(major == 1) {
		// dates
		n_tests = 3;
	}
	else if(major == 2) {
		// strings
		n_tests = 16;
	}
	else {
		printf("UNKNOWN TEST SUITE!\n");
	}

	if(n_tests > 0) {
		if(minor == -1) {
			for(int i=0;i<n_tests;i++) {
				run_test(major,i);
			}
		}
		else {
			run_test(major,minor);
		}
	}
}

void tests(int major,int minor) {
	printf("RUNNING TESTS\n");
	printf("-------------\n");
	printf("Major = %d\n",major);
	printf("Minor = %d\n",minor);
	printf("\n");
	const int n_test_sets = 3;
	if(major == -1) {
		for(int i=0;i<n_test_sets;i++) {
			run_test_set(i,-1);
		}
	}
	else {
		run_test_set(major,minor);
	}
	printf("\n");
	printf("--------------\n");
	printf("TESTS COMPLETE\n");
}
