/*
 * MultiMail offline mail reader
 * Blue Wave class

 Copyright (c) 1996 Toth Istvan <stoty@vma.bme.hu>
 Copyright (c) 1999 William McBrine <wmcbrine@clark.net>

 Distributed under the GNU General Public License.
 For details, see the file COPYING in the parent directory. */

#include "bw.h"
#include "compress.h"
#include "../interfac/mysystem.h"

// get a little-endian short, return an int
unsigned getshort(const unsigned char *x)
{
	return ((unsigned) x[1] << 8) + (unsigned) x[0];
}

// get a little-endian long
unsigned long getlong(const unsigned char *x)
{
	return ((unsigned long) x[3] << 24) + ((unsigned long) x[2] << 16) +
		((unsigned long) x[1] << 8) + (unsigned long) x[0];
}

// put an int into a little-endian short
void putshort(unsigned char *dest, unsigned source)
{
	dest[0] = source & 0xff;
	dest[1] = (source & 0xff00) >> 8;
}

// put a long into a little-endian long
void putlong(unsigned char *dest, unsigned long source)
{
	dest[0] = source & 0xff;
	dest[1] = (source & 0xff00) >> 8;
	dest[2] = (source & 0xff0000) >> 16;
	dest[3] = (source & 0xff000000) >> 24;
}

// ---------------------------------------------------------------------------
// The Blue Wave methods
// ---------------------------------------------------------------------------

bluewave::bluewave(mmail *mmA)
{
	mm = mmA;
	init();
}

void bluewave::init()
{
	ID = 0;
	bodyString = 0;

	findInfBaseName();

	initInf();
	initMixID();

	body = new struct bodytype *[maxConf];

	ftiFile = openFile("FTI");
	datFile = openFile("DAT");
}

bluewave::~bluewave()
{
	cleanup();
}

void bluewave::cleanup()
{
	delete[] bulletins;

	while (maxConf)
		delete[] body[--maxConf];
	delete[] body;
	delete[] mixID;
	delete[] areas;
	delete[] mixRecord;
	delete[] bodyString;

	fclose(ftiFile);
	fclose(datFile);
}

int bluewave::getNoOfAreas()
{
	return maxConf;
}

void bluewave::resetAll()
{
	cleanup();
	init();
}

area_header *bluewave::getNextArea()
{
	int x, totmsgs, numpers, flags_raw, flags_cooked;

	x = mixID[ID];

	if (x != -1) {
		totmsgs = getshort(mixRecord[x].totmsgs);
		numpers = getshort(mixRecord[x].numpers);
	} else
		totmsgs = numpers = 0;

	body[ID] = new struct bodytype[totmsgs];

	flags_raw = getshort(areas[ID].area_flags);
	flags_cooked = ((x != -1) ? ACTIVE : 0) |
		((flags_raw & INF_ALIAS_NAME) ? ALIAS : 0) |
		((flags_raw & INF_NETMAIL) ? NETMAIL : 0) |
		((areas[ID].network_type == INF_NET_INTERNET) ?
		(INTERNET | LATINCHAR) : 0) |
		((flags_raw & INF_NO_PUBLIC) ? 0 : PUBLIC) |
		((flags_raw & INF_NO_PRIVATE) ? 0 : PRIVATE);

	area_header *tmp = new area_header(mm,
		ID + mm->driverList->getOffset(this),
		(char *) areas[ID].areanum, (char *) areas[ID].echotag,
		(char *) areas[ID].title, "Blue Wave", flags_cooked,
		totmsgs, numpers, from_to_len);
	ID++;
	return tmp;
}

void bluewave::selectArea(int area)
{
	currentArea = area;
	resetLetters();
}

void bluewave::resetLetters()
{
	currentLetter = 0;
}

int bluewave::getNoOfLetters()
{
	int x = mixID[currentArea];

	return ((x != -1) ? getshort(mixRecord[x].totmsgs) : 0);
}

letter_header *bluewave::getNextLetter()
{
	FTI_REC ftiRec;

	if (currentLetter >= (int) getshort(
		mixRecord[mixID[currentArea]].totmsgs))
			return 0;

	fseek(ftiFile, getlong(mixRecord[mixID[currentArea]].msghptr) +
		currentLetter * ftiStructLen, SEEK_SET);

	if (!(fread(&ftiRec, sizeof(FTI_REC), 1, ftiFile)))
		fatalError("Error reading .FTI file");

	long msglength = getlong(ftiRec.msglength);

	body[currentArea][currentLetter].pointer = getlong(ftiRec.msgptr);
	body[currentArea][currentLetter].msgLength = msglength;

	cropesp((char *) ftiRec.from);
	cropesp((char *) ftiRec.to);
	cropesp((char *) ftiRec.subject);

	int flags = getshort(ftiRec.flags);

	net_address na;
	na.zone = getshort(ftiRec.orig_zone);
	if (na.zone) {
		na.net = getshort(ftiRec.orig_net);
		na.node = getshort(ftiRec.orig_node);
		na.point = 0;	// set from getBody()
		na.isSet = true;
	}

	return new letter_header(mm, stripre((char *) ftiRec.subject),
		(char *) ftiRec.to, (char *) ftiRec.from, (char *) ftiRec.date,
		0, getshort(ftiRec.replyto), currentLetter++,
		getshort(ftiRec.msgnum), currentArea,
		!(!(flags & MSG_NET_PRIVATE)), msglength, this, na,
		(areas[currentArea].network_type == INF_NET_INTERNET));
}

// Find a hidden line and return its contents
const char *bluewave::getHidden(const char *pattern, char *&end)
{
	const char *s = strstr(bodyString, pattern);
	if (s) {
		s += strlen(pattern);
		end = strchr(s, '\n');
		if (end)
			*end = '\0';
	}
	return s;
}

// returns the body of the requested letter in the active area
const char *bluewave::getBody(letter_header &mhead)
{
	unsigned char *p;
	const char *s;
	char *end;
	int c, kar, AreaID, LetterID;

	AreaID = mhead.getAreaID() - mm->driverList->getOffset(this);
	LetterID = mhead.getLetterID();

	delete[] bodyString;
	bodyString = new char[body[AreaID][LetterID].msgLength + 1];
	fseek(datFile, body[AreaID][LetterID].pointer, SEEK_SET);

	for (c = 0, p = (unsigned char *) bodyString;
	     c < body[AreaID][LetterID].msgLength; c++) {
		kar = fgetc(datFile);

		// Skip leading space, if present:
		if (!c && (kar == ' '))
			kar = fgetc(datFile);

		// Some buggy packets:
		if (!kar || (kar == EOF))
			kar = ' ';

		if (kar == '\r')
			*p++ = '\n';
		else
			if (kar != '\n')
				*p++ = kar;
	}
        do
                p--;
        while ((*p == ' ') || (*p == '\n'));    // Strip blank lines
        *++p = '\0';

	net_address &na = mhead.getNetAddr();

	if (areas[AreaID].network_type == INF_NET_INTERNET) {

		// Get address from "From:" line:
		if (!na.isSet) {
			if ((s = getHidden("\001From: ", end))) {
				na.isInternet = true;
				na = fromAddr(s);
				if (end)
					*end = '\n';
			}
		}

		// Get Message-ID/References:
		if (!mhead.getMsgID()) {
			if ((s = getHidden("\001Message-ID: ", end))) {
				const char *s2;
				char *end2, *final;

				if (end)
					*end = '\n';
				s2 = getHidden("\001References: ", end2);
				if (end)
					*end = '\0';

				if (s2) {
					final = new char[strlen(s) +
						strlen(s2) + 2];
					sprintf(final, "%s %s", s2, s);
					if (end2)
						*end2 = '\n';
				} else
					final = (char *) s;

				mhead.changeMsgID(final);
				if (s2)
					delete[] final;
				if (end)
					*end = '\n';
			}
		}

	} else {

		// Add point to netmail address, if possible/necessary:
		if (na.isSet)
			if (!na.point) {
				s = strstr(bodyString, "\001FMPT");
				if (s)
					sscanf(s, "\001FMPT%d\n", &na.point);
			}

		// Get MSGID:
		if (!mhead.getMsgID())
			if ((s = getHidden("\001MSGID: ", end))) {
				mhead.changeMsgID(s);
				*end = '\n';
			}

		// Change to Latin character set, if necessary:
		s = strstr(bodyString, "\001CHRS: L");

		if (!s)
			s = strstr(bodyString, "\001CHARSET: L");
		if (s)
			mhead.setLatin(true);
	}

	return bodyString;
}

file_header *bluewave::getFileList()
{
	return mm->workList->existsF("newfiles.");
}

void bluewave::listBulletins(INF_HEADER &infoHeader)
{
	const char *q;

	file_list *wl = mm->workList;
	int filecount = 0;

	bulletins = new file_header *[wl->getNoOfFiles() + 1];

	for (int c = 0; c < 5; c++) {
		q = (const char *) (infoHeader.readerfiles[c]);
		if (*q)
			wl->addItem(bulletins, q, filecount);
	}

	wl->addItem(bulletins, "blt", filecount);
	wl->addItem(bulletins, ".txt", filecount);

	if (filecount)
		bulletins[filecount] = 0;
	else {
		delete[] bulletins;
		bulletins = 0;
	}
}

file_header **bluewave::getBulletins()
{
	return bulletins;
}

void bluewave::findInfBaseName()
{
	const char *q = mm->workList->exists(".inf");
	int len = strlen(q) - 4;
	strncpy(packetBaseName, q, len);
	packetBaseName[len] = '\0';
}

// Read .INF file
void bluewave::initInf()
{
	INF_HEADER infoHeader;

	FILE *infFile = openFile("INF");

	// Header

	if (!fread(&infoHeader, sizeof(INF_HEADER), 1, infFile))
		fatalError("Error reading .INF file");

	infoHeaderLen = getshort(infoHeader.inf_header_len);
	if (infoHeaderLen < ORIGINAL_INF_HEADER_LEN)
		infoHeaderLen = ORIGINAL_INF_HEADER_LEN;

	infoAreainfoLen = getshort(infoHeader.inf_areainfo_len);
	if (infoAreainfoLen < ORIGINAL_INF_AREA_LEN)
		infoAreainfoLen = ORIGINAL_INF_AREA_LEN;

	maxConf = (mm->workList->getSize() - infoHeaderLen) / infoAreainfoLen;

	from_to_len = infoHeader.from_to_len;
	if (!from_to_len || (from_to_len > 35))
		from_to_len = 35;

	mixStructLen = getshort(infoHeader.mix_structlen);
	if (mixStructLen < ORIGINAL_MIX_STRUCT_LEN)
		mixStructLen = ORIGINAL_MIX_STRUCT_LEN;

	ftiStructLen = getshort(infoHeader.fti_structlen);
	if (ftiStructLen < ORIGINAL_FTI_STRUCT_LEN)
		ftiStructLen = ORIGINAL_FTI_STRUCT_LEN;

	mm->resourceObject->set(LoginName, (char *) infoHeader.loginname);
	mm->resourceObject->set(AliasName, (char *) infoHeader.aliasname);
	mm->resourceObject->set(SysOpName, (char *) infoHeader.sysop);
	mm->resourceObject->set(BBSName, (char *) infoHeader.systemname); 

	// Areas

	areas = new INF_AREA_INFO[maxConf];
	mixID = new int[maxConf];

	for (int d = 0; d < maxConf; d++) {
		fseek(infFile, (infoHeaderLen + d * infoAreainfoLen),
			SEEK_SET);
		if (!fread(&areas[d], sizeof(INF_AREA_INFO), 1, infFile))
			fatalError("Premature EOF in bluewave::initInf");
		mixID[d] = -1;
	}

	fclose(infFile);

	// Bulletins, etc.

	listBulletins(infoHeader);
}

// Read .MIX file, match .INF records to .MIX records
void bluewave::initMixID()
{
	// Read .MIX file

	FILE *mixFile = openFile("MIX");

	noOfMixRecs = (int) (mm->workList->getSize() / mixStructLen);
	mixRecord = new MIX_REC[noOfMixRecs];

	fread(mixRecord, mixStructLen, noOfMixRecs, mixFile);
	fclose(mixFile);

	// Match records

	int mixIndex = -1, c, d;

	for (c = 0; c < noOfMixRecs; c++)
		for (d = mixIndex + 1; d < maxConf; d++)
			if (!strncasecmp((char *) areas[d].areanum,
			(char *) mixRecord[c].areanum, 6)) {
				mixID[d] = c;
				mixIndex = d;
				break;
			}
}

FILE *bluewave::openFile(const char *extent)
{
	FILE *tmp;
	char fname[13];

	sprintf(fname, "%s.%s", packetBaseName, extent);

	if (!(tmp = mm->workList->ftryopen(fname, "rb"))) {
		sprintf(fname, "Could not open .%s file", extent);
		fatalError(fname);
	}
	return tmp;
}

// ---------------------------------------------------------------------------
// The Blue Wave reply methods
// ---------------------------------------------------------------------------

bwreply::bwreply(mmail *mmA)
{
	mm = mmA;
	init();
}

bwreply::~bwreply()
{
	if (replyExists)
		cleanup();
}

void bwreply::init()
{
	uplHeader = new UPL_HEADER;
	replyText = 0;
	newFileName(mm->resourceObject->get(PacketName));
	mychdir(mm->resourceObject->get(ReplyDir));
	replyExists = !access(replyPacketName, R_OK | W_OK);

	if (replyExists) {
		uncompress();
		readUpl();
		currentLetter = 1;
	} else {
		uplListHead = 0;
		noOfLetters = currentLetter = 0;
	}
}

void bwreply::cleanup()
{
	upl_list *next, *curr = uplListHead;

	for (int c = 0; c < noOfLetters; c++) {
		remove(curr->fname);
		delete[] curr->msgid;
		next = curr->nextRecord;
		delete curr;
		curr = next;
	}
	delete[] replyText;
	delete uplHeader;
}

void bwreply::newFileName(const char *bwFile)
{
	int c;

	for (c = 0; (bwFile[c] != '.') && bwFile[c]; c++)
		replyPacketName[c] = tolower(bwFile[c]);
	strcpy(replyPacketName + c, ".new");
}

void bwreply::uncompress()
{
	char fname[256];

	sprintf(fname, "%s/%s", mm->resourceObject->get(ReplyDir),
		replyPacketName);
	uncompressFile(mm->resourceObject, fname,
		mm->resourceObject->get(UpWorkDir));
}

bool bwreply::getNew1(FILE *uplFile, upl_list *l, int recnum)
{
	FILE *orgfile, *destfile;
	const char *orgname;
	int c, count = 0;

	fseek(uplFile, getshort(uplHeader->upl_header_len) + recnum *
		getshort(uplHeader->upl_rec_len), SEEK_SET);
	if (fread(&(l->uplRec), sizeof(UPL_REC), 1, uplFile) != 1)
		return false;

	orgname = upWorkList->exists((char *) l->uplRec.filename);
	mytmpnam(l->fname);

	l->msgid = 0;

	if ((orgfile = fopen(orgname, "rb"))) {
	    if ((destfile = fopen(l->fname, "wt"))) {
		while ((c = fgetc(orgfile)) != EOF) {
			if (c == '\001') {
				c = fgetc(orgfile);
				int x;
				bool isRef = (c == 'R');

				if (isRef) {
					for (x = 0; x < 11; x++)
						fgetc(orgfile);
				}
				x = 0;
				while ((c != EOF) && (c != '\n')) {
					c = fgetc(orgfile);
					x++;
				}
				if (isRef) {
					l->msgid = new char[x];
					fseek(orgfile, x * -1, SEEK_CUR);
					fread(l->msgid, 1, x, orgfile);
					strtok(l->msgid, "\r");
				}
				c = '\r';
			}
			if (c != '\r') {
				fputc(c, destfile);
				count++;
			}
		}
		fclose(destfile);
	    }
	    fclose(orgfile);
	}

	l->msglen = count;

	remove(orgname);

	return true;
}

void bwreply::readUpl()
{
	FILE *uplFile;
	upl_list baseUplList, *currUplList;
	char uplName[13];

	upWorkList = new file_list(mm->resourceObject->get(UpWorkDir));
	sprintf(uplName, "%s.upl", findBaseName(replyPacketName));

	if (!(uplFile = upWorkList->ftryopen(uplName, "rb")))
		fatalError("Error opening UPL");

	if (!fread(uplHeader, sizeof(UPL_HEADER), 1, uplFile))
		fatalError("Error reading UPL file");

	noOfLetters = (upWorkList->getSize() -
			getshort(uplHeader->upl_header_len)) /
			getshort(uplHeader->upl_rec_len);

	currUplList = &baseUplList;
	for (int c = 0; c < noOfLetters; c++) {
		currUplList->nextRecord = new upl_list;
		currUplList = currUplList->nextRecord;
		if (!getNew1(uplFile, currUplList, c)) {    // ha eof/error
			delete currUplList;
			break;
		}
	}
	uplListHead = baseUplList.nextRecord;

	fclose(uplFile);
	remove(upWorkList->exists(uplName));
	delete upWorkList;
}

int bwreply::getNoOfAreas()
{
	return 1;
}

void bwreply::resetAll()
{
	cleanup();
	init();
}

area_header *bwreply::getNextArea()
{
	return new area_header(mm, 0, "REPLY", "REPLIES",
		"Letters written by you", "Blue Wave replies",
		(COLLECTION | REPLYAREA | ACTIVE | PUBLIC | PRIVATE),
		noOfLetters, 0, 35);
}

void bwreply::selectArea(int ID)
{
	if (ID == 0)
		resetLetters();
}

void bwreply::resetLetters()
{
	currentLetter = 1;
	uplListCurrent = uplListHead;
}

int bwreply::getNoOfLetters()
{
	return noOfLetters;
}

letter_header *bwreply::getNextLetter()
{
	time_t unixTime = (time_t) getlong(uplListCurrent->uplRec.unix_date);

	char date[30];
	strftime(date, 30, "%b %d %Y  %H:%M", localtime(&unixTime));

	int areaNo = 0;
	for (int c = 0; c < mm->areaList->noOfAreas(); c++)
		if (!strcmp(mm->areaList->getName(c),
			    (char *) uplListCurrent->uplRec.echotag)) {
			areaNo = c;
			break;
		}

	const char *msgid;
	net_address na;
	bool isInet = (uplListCurrent->uplRec.network_type ==
		INF_NET_INTERNET);
	if (isInet) {
		na.isInternet = true;
		na = (char *) uplListCurrent->uplRec.net_dest;
		msgid = uplListCurrent->msgid;
	} else {
		na.zone = getshort(uplListCurrent->uplRec.destzone);
		if (na.zone) {
			na.net = getshort(uplListCurrent->uplRec.destnet);
			na.node = getshort(uplListCurrent->uplRec.destnode);
			na.point = getshort(uplListCurrent->uplRec.destpoint);
			na.isSet = true;
		}

		msgid = (char *) uplListCurrent->uplRec.net_dest;
		if (!strncasecmp(msgid, "REPLY: ", 7))
			msgid += 7;
		else
			msgid = 0;
	}

	letter_header *newLetter = new letter_header(mm,
					(char *) uplListCurrent->uplRec.subj,
					(char *) uplListCurrent->uplRec.to,
				(char *) uplListCurrent->uplRec.from, date,
				msgid, getlong(uplListCurrent->uplRec.replyto),
					currentLetter, currentLetter, areaNo,
		!(!(getshort(uplListCurrent->uplRec.msg_attr) & UPL_PRIVATE)),
					uplListCurrent->msglen, this, na,
								isInet);

	currentLetter++;
	uplListCurrent = uplListCurrent->nextRecord;
	return newLetter;
}

const char *bwreply::getBody(letter_header &mhead)
{
	FILE *replyFile;
	upl_list *actUplList;
	int msglen;

	int ID = mhead.getLetterID();

	delete[] replyText;

	actUplList = uplListHead;
	for (int c = 1; c < ID; c++)
		actUplList = actUplList->nextRecord;

	if ((replyFile = fopen(actUplList->fname, "rt"))) {
		msglen = actUplList->msglen;
		replyText = new char[msglen + 1];
		msglen = fread(replyText, 1, msglen, replyFile);
		fclose(replyFile);
		replyText[msglen] = '\0';
	} else
		replyText = 0;

	return replyText;
}

file_header *bwreply::getFileList()
{
	return 0;
}

file_header **bwreply::getBulletins()
{
	return 0;
}

void bwreply::enterLetter(letter_header &newLetter,
				const char *newLetterFileName, int length)
{
	upl_list *newList = new upl_list;
	memset(newList, 0, sizeof(upl_list));

	int msg_attr = 0;

	// fill the fields of UPL_REC
	strncpy((char *) newList->uplRec.from, newLetter.getFrom(), 35);
	strncpy((char *) newList->uplRec.to, newLetter.getTo(), 35);
	strncpy((char *) newList->uplRec.subj, newLetter.getSubject(), 71);
	strcpy((char *) newList->uplRec.echotag, mm->areaList->getName());

	bool usenet = mm->areaList->isUsenet();
	bool internet = mm->areaList->isInternet();

	if (internet || usenet)
		newList->uplRec.network_type = INF_NET_INTERNET;

	const char *msgid = newLetter.getMsgID();
	if (msgid)
		if (!(internet || usenet))
			sprintf((char *) newList->uplRec.net_dest,
				"REPLY: %.92s", msgid);
		else
			if (usenet)
				newList->msgid = strdupplus(msgid);

	strcpy(newList->fname, newLetterFileName);
	putlong(newList->uplRec.unix_date, (long) time(0));

	net_address &na = newLetter.getNetAddr();
	if (na.isSet) {
		msg_attr |= UPL_NETMAIL;
		if (na.isInternet)
			sprintf((char *) newList->uplRec.net_dest,
				"%.99s", (const char *) na);
		else {
			putshort(newList->uplRec.destzone, na.zone);
			putshort(newList->uplRec.destnet, na.net);
			putshort(newList->uplRec.destnode, na.node);
			putshort(newList->uplRec.destpoint, na.point);
		}
	}

	putlong(newList->uplRec.replyto,
		(unsigned long) newLetter.getReplyTo());

	if (newLetter.getPrivate())
		msg_attr |= UPL_PRIVATE;
	if (newLetter.getReplyTo())
		msg_attr |= UPL_IS_REPLY;
	putshort(newList->uplRec.msg_attr, msg_attr);

	newList->msglen = length;

	if (!noOfLetters)
		uplListHead = newList;
	else {
		upl_list *workList = uplListHead;
		for (int c = 1; c < noOfLetters; c++)	//go to last elem
			workList = workList->nextRecord;
		workList->nextRecord = newList;
	}

	noOfLetters++;
	replyExists = true;
}

void bwreply::killLetter(int letterNo)
{
	upl_list *actUplList, *tmpUplList;

	if ((noOfLetters == 0) || (letterNo < 1) || (letterNo > noOfLetters))
		fatalError("Internal error in bwreply::killLetter");

	if (letterNo == 1) {
		tmpUplList = uplListHead;
		uplListHead = uplListHead->nextRecord;
	} else {
		actUplList = uplListHead;
		for (int c = 1; c < letterNo - 1; c++)
			actUplList = actUplList->nextRecord;
		tmpUplList = actUplList->nextRecord;
		actUplList->nextRecord = (letterNo == noOfLetters) ? 0 :
			actUplList->nextRecord->nextRecord;
	}
	noOfLetters--;
	remove(tmpUplList->fname);
	delete[] tmpUplList->msgid;
	delete tmpUplList;
	resetLetters();
}

area_header *bwreply::refreshArea()
{
	return getNextArea();
}

void bwreply::addNew1(FILE *uplFile, upl_list *l)
{
	FILE *orgfile, *destfile;
	const char *dest;
	int c;

	dest = freeFileName();
	strcpy((char *) l->uplRec.filename, dest);

	if ((orgfile = fopen(l->fname, "rt"))) {
		if ((destfile = fopen(dest, "wb"))) {
			if (l->uplRec.network_type == INF_NET_INTERNET) {
				fprintf(destfile, *(l->uplRec.net_dest) ?
					"\001X-Mail" : "\001X-News");
				fprintf(destfile, "reader: " MM_NAME
					" Offline Reader for %s v%d.%d\r\n",
					sysname(), MM_MAJOR, MM_MINOR);

				if (l->msgid)
					fprintf(destfile,
						"\001References: %s\r\n",
							l->msgid);
			}

			while ((c = fgetc(orgfile)) != EOF) {
				if (c == '\n')
					fputc('\r', destfile);
				fputc(c, destfile);
			}
			fclose(destfile);
		}
		fclose(orgfile);
	}
	fwrite(&(l->uplRec), sizeof(UPL_REC), 1, uplFile);
}

void bwreply::makeReply()
{
	FILE *uplFile;
	UPL_HEADER newUplHeader;
	upl_list *actUplList;
	char uplFileName[13], tmp[256];

	if (mychdir(mm->resourceObject->get(UpWorkDir)))
		fatalError("Could not cd to upworkdir in bwreply::makeReply");

	// Delete old file -- hmm, could trash old replies if the pack fails
	sprintf(tmp, "%s/%s", mm->resourceObject->get(ReplyDir),
		replyPacketName);
	remove(tmp);

	if (!noOfLetters)
		return;

	// fill the UPL_HEADER struct
	memset(&newUplHeader, 0, sizeof(UPL_HEADER));

	putshort(newUplHeader.upl_header_len, sizeof(UPL_HEADER));
	putshort(newUplHeader.upl_rec_len, sizeof(UPL_REC));

	newUplHeader.reader_major = MM_MAJOR;
	newUplHeader.reader_minor = MM_MINOR;
	sprintf((char *) newUplHeader.vernum, "%d.%d", MM_MAJOR, MM_MINOR);
	for (int c = 0; newUplHeader.vernum[c]; newUplHeader.vernum[c++] -= 10);

	int tearlen = sprintf((char *) newUplHeader.reader_name,
		MM_NAME "/%s", sysname());
	strncpy((char *) newUplHeader.reader_tear, ((tearlen < 17) ?
		(char *) newUplHeader.reader_name : MM_NAME), 16);

	strcpy((char *) newUplHeader.loginname,
		mm->resourceObject->get(LoginName));
	strcpy((char *) newUplHeader.aliasname,
		mm->resourceObject->get(AliasName));

	sprintf(uplFileName, "%s.upl",
		findBaseName(mm->resourceObject->get(PacketName)));
	uplFile = fopen(uplFileName, "wb");	//!! no check yet

	fwrite(&newUplHeader, sizeof(UPL_HEADER), 1, uplFile);

	actUplList = uplListHead;
	for (int c = 0; c < noOfLetters; c++) {
		addNew1(uplFile, actUplList);
		actUplList = actUplList->nextRecord;
	}

	fclose(uplFile);

	//pack the files
	sprintf(tmp, "%s *.msg", uplFileName);
	compressAddFile(mm->resourceObject,
		mm->resourceObject->get(ReplyDir), replyPacketName, tmp);

	// clean up the work area
	clearDirectory(".");
}

const char *bwreply::freeFileName()
{
	static char testFileName[13];

	for (long c = 0; c <= 99999; c++) {
		sprintf(testFileName, "%05ld.msg", c);
		if (access(testFileName, R_OK) == -1)
			break;
	}
	return testFileName;
}
