/*
 * MultiMail offline mail reader
 * QWK

 Copyright (c) 1997 John Zero <john@graphisoft.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 "qwk.h"
#include "compress.h"
#include "../interfac/mysystem.h"

unsigned char *onecomp(unsigned char *p, char *dest, const char *comp)
{
	int len = strlen(comp);

	if (!strncasecmp((char *) p, comp, len)) {
		p += len;
		while (*p == ' ')
			p++;
		int x;
		for (x = 0; *p && (*p != '\n') && (x < 71); x++)
			dest[x] = *p++;
		dest[x] = '\0';

		while (*p == '\n')
			p++;
		return p;
	}
	return 0;
}

// ---------------------------------------------------------------------------
// The qheader methods
// ---------------------------------------------------------------------------

bool qheader::init(FILE *datFile)
{
	qwkmsg_header qh;
	char buf[9];

	if (!fread(&qh, 1, sizeof qh, datFile))
		return false;

	getQfield(from, qh.from, 25);
	getQfield(to, qh.to, 25);
	getQfield(subject, qh.subject, 25);

	cropesp(from);
	cropesp(to);
	cropesp(subject);

	getQfield(date, qh.date, 8);
	date[2] = '-';
	date[5] = '-';		// To deal with some broken messages
	strcat(date, " ");

	getQfield(buf, qh.time, 5);
	strcat(date, buf);

	getQfield(buf, qh.refnum, 8);
	refnum = atoi(buf);

	getQfield(buf, qh.msgnum, 7);
	msgnum = atoi(buf);

	getQfield(buf, qh.chunks, 6);
	msglen = (atoi(buf) - 1) << 7;

	privat = (qh.status == '*') || (qh.status == '+');

	origArea = ((int) qh.confMSB << 8) + (int) qh.confLSB;

	return true;
}

void qheader::output(FILE *repFile)
{
	qwkmsg_header qh;
	char buf[10];
	int chunks, length, sublen;

	length = msglen;
	sublen = strlen(subject);
	if (sublen > 25) {
		length += sublen + 11;
		sublen = 25;
	}

	memset(&qh, ' ', sizeof qh);

	chunks = (length + 127) / 128;
	if (!chunks)
		chunks = 1;

	sprintf(buf, " %-6d", msgnum);
	strncpy(qh.msgnum, buf, 7);
	if (refnum) {
		sprintf(buf, " %-7d", refnum);
		strncpy(qh.refnum, buf, 8);
	}
	strncpy(qh.to, to, strlen(to));
	strncpy(qh.from, from, strlen(from));
	strncpy(qh.subject, subject, sublen);

	qh.alive = (char) 0xE1;

	strncpy(qh.date, date, 8);
	strncpy(qh.time, &date[9], 5);

	sprintf(buf, "%-6d", chunks + 1);
	strncpy(qh.chunks, buf, 6);
	if (privat)
		qh.status = '*';

	fwrite(&qh, 1, sizeof qh, repFile);
}

// ---------------------------------------------------------------------------
// The QWK methods
// ---------------------------------------------------------------------------

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

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

	qwke = !(!mm->workList->exists("toreader.ext"));

	readControlDat();
	if (qwke)
		readToReader();
	initMessagesDat();
	readIndices();

	listBulletins();
}

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

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

	while (maxConf--) {
		delete[] body[maxConf];
		delete[] areas[maxConf].name;
	}
	delete[] body;
	delete[] areas;
	delete[] bodyString;

	fclose(msgdatFile);
}

unsigned long qwkpack::MSBINtolong(unsigned const char *ms)
{
	return ((((unsigned long) ms[0] + ((unsigned long) ms[1] << 8) +
		  ((unsigned long) ms[2] << 16)) | 0x800000L) >>
			(24 + 0x80 - ms[3]));
}

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

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

area_header *qwkpack::getNextArea()
{
	int cMsgNum = areas[ID].nummsgs;
	bool x = (areas[ID].num == -1);

	area_header *tmp = new area_header(mm,
			ID + mm->driverList->getOffset(this),
			areas[ID].numA, areas[ID].name,
			(x ? "Letters addressed to you" : areas[ID].name),
			(qwke ? (x ? "QWKE personal" : "QWKE") :
			(x ? "QWK personal" : "QWK")),
			areas[ID].attr | (x ? COLLECTION : 0) |
			(cMsgNum ? ACTIVE : 0), cMsgNum, 0, 25);
	ID++;
	return tmp;
}

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

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

int qwkpack::getNoOfLetters()
{
	return areas[currentArea].nummsgs;
}

const char *qwkpack::getBBSID()
{
	return BBSID;
}

bool qwkpack::isQWKE()
{
	return qwke;
}

void qwkpack::readIndices()
{
	const char *p;
	int x, cMsgNum;

	body = new struct bodytype *[maxConf];

	for (x = 0; x < maxConf; x++) {
		body[x] = 0;
		areas[x].nummsgs = 0;
	}

	if ((p = mm->workList->exists(".ndx"))) {
		struct {
        		unsigned char MSB[4];
        		unsigned char confnum;
		} ndx_rec;

		FILE *idxFile;
		char fname[13];

		while (p) {
			cMsgNum = 0;

			strncpy(fname, p, strlen(p) - 4);
			fname[strlen(p) - 4] = '\0';
			x = atoi(fname);
			if (!x)
				if (!strcasecmp(fname, "personal"))
					x = -1;
			x = getXNum(x);

			if ((idxFile = mm->workList->ftryopen(p, "rb"))) {
				cMsgNum = mm->workList->getSize() / ndxRecLen;
				body[x] = new struct bodytype[cMsgNum];
				for (int y = 0; y < cMsgNum; y++) {
					fread(&ndx_rec, ndxRecLen, 1, idxFile);
					body[x][y].pointer =
						MSBINtolong(ndx_rec.MSB);
				}
				fclose(idxFile);
			}

			areas[x].nummsgs = cMsgNum;
			p = mm->workList->getNext(".ndx");
		}
	} else {	// if no .ndx files exist
		struct ndx_fake {
			int confnum, pointer;
		};

		qheader qHead;
		struct ndx_fake *tmpndx = new struct ndx_fake[numMsgs];
		int counter, current = 0;

		fseek(msgdatFile, 128, SEEK_SET);
		while ((current < numMsgs) && qHead.init(msgdatFile)) {
			counter = ftell(msgdatFile) >> 7;
			x = getXNum(qHead.origArea);
			tmpndx[current].confnum = x;
			tmpndx[current++].pointer = counter;
			areas[x].nummsgs++;
			fseek(msgdatFile, qHead.msglen, SEEK_CUR);
		}
		numMsgs = current;

		for (x = 0; x < maxConf; x++) {
			cMsgNum = areas[x].nummsgs;
			if (cMsgNum) {
				body[x] = new struct bodytype[cMsgNum];
				areas[x].nummsgs = 0;
			}
		}

		for (x = 0; x < numMsgs; x++) {
			current = tmpndx[x].confnum;
			body[current][areas[current].nummsgs++].pointer =
				tmpndx[x].pointer;
		}

		delete[] tmpndx;
	}
}

int qwkpack::getXNum(int area)
{
	int c;

	for (c = 0; c < maxConf; c++)
		if (areas[c].num == area)
			break;
	return c;
}

int qwkpack::getYNum(int area, unsigned long rawpos)
{
	int c;

	for (c = 0; c < areas[area].nummsgs; c++)
		if ((unsigned) body[area][c].pointer == rawpos)
			break;
	return c;
}

letter_header *qwkpack::getNextLetter()
{
	qheader q;
	unsigned long pos, rawpos;
	int areaID, letterID;

	rawpos = body[currentArea][currentLetter].pointer;
	pos = (rawpos - 1) << 7;

	fseek(msgdatFile, pos, SEEK_SET);
	if (!q.init(msgdatFile))
		fatalError("Error reading MESSAGES.DAT");

	if (areas[currentArea].num == -1) {
		areaID = getXNum(q.origArea);
		letterID = getYNum(areaID, rawpos);
	} else {
		areaID = currentArea;
		letterID = currentLetter;
	}

	body[areaID][letterID].msgLength = q.msglen;

	currentLetter++;

	static net_address nullNet;

	return new letter_header(mm, stripre(q.subject), q.to, q.from,
			q.date, 0, q.refnum, letterID, q.msgnum, areaID,
			q.privat, q.msglen, this, nullNet);
}

// returns the body of the requested letter in the active area
const char *qwkpack::getBody(letter_header &mhead)
{
	char extsubj[72];	// extended subject line
	unsigned char *p, *q;
	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(msgdatFile, body[AreaID][LetterID].pointer << 7, SEEK_SET);

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

		if (!kar)
			kar = ' ';

		*p++ = (kar == 227) ? '\n' : kar;	
	}
	do
		p--;
	while ((*p == ' ') || (*p == '\n'));	// Strip blank lines
	*++p = '\0';

	// Get extended (QWKE-type) info, if available:

	p = (unsigned char *) bodyString;
	bool anyfound;

	do {
		anyfound = false;

		q = onecomp(p, extsubj, "subject:");

		if (!q) {
			q = onecomp(p + 1, extsubj, "@subject:");
			if (q) {
				extsubj[strlen(extsubj) - 1] = '\0';
				cropesp(extsubj);
			}
		}

		// For WWIV QWK door:
		if (!q)
			q = onecomp(p, extsubj, "title:");

		if (q) {
			p = q;
			mhead.changeSubject(stripre(extsubj));
			anyfound = true;
		}

		q = onecomp(p, extsubj, "to:");
		if (q) {
			p = q;
			mhead.changeTo(extsubj);
			anyfound = true;
		}

		q = onecomp(p, extsubj, "from:");
		if (q) {
			p = q;
			mhead.changeFrom(extsubj);
			anyfound = true;
		}

	} while (anyfound);

	// Change to Latin character set, if necessary:
	const char *s = strstr(bodyString, "\001CHRS: L");
	if (!s)
		s = strstr(bodyString, "\001CHARSET: L");
	if (s)
		mhead.setLatin(true);

	return (char *) p;
}

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

void qwkpack::listBulletins()
{
	file_list *wl = mm->workList;
	int filecount = 0;

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

	for (int c = 0; c < 3; c++)
		if (textfiles[c][0])
			wl->addItem(bulletins, textfiles[c], filecount);

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

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

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

char *qwkpack::nextLine()
{
	static char line[128];

	fgets(line, sizeof line, ctrdatFile);
	strtok(line, "\r\n");
	return line;
}

void qwkpack::readControlDat()
{
	char *p, *q;
	int pers;

	if (!(ctrdatFile = mm->workList->ftryopen("control.dat", "rb")))
		fatalError("Could not open CONTROL.DAT");

	mm->resourceObject->set(BBSName, nextLine());	// 1: BBS name
	nextLine();					// 2: city/state
	nextLine();					// 3: phone#

	q = nextLine();					// 4: sysop's name
	int slen = strlen(q);
	if (slen > 6) {
		p = q + slen - 5;
		if (!strcasecmp(p, "Sysop")) {
			if (*--p == ' ')
				p--;
			if (*p == ',')
				*p = '\0';
		}
	}
	mm->resourceObject->set(SysOpName, q);

	q = nextLine();					// 5: doorserno,BBSid
	p = strtok(q, ",");
	p = strtok(0, " ");
	strncpy(BBSID, p, 8);
	BBSID[8] = '\0';

	nextLine();					// 6: time&date

	p = nextLine();					// 7: user's name
	cropesp(p);
	mm->resourceObject->set(LoginName, p);
	mm->resourceObject->set(AliasName, p);

	nextLine();					// 8: blank/any
	nextLine();					// 9: anyth.

	numMsgs = atoi(nextLine());			// 10: # messages
							// (not reliable)
	maxConf = atoi(nextLine()) + 1;			// 11: Max conf #

	pers = !(!mm->workList->exists("personal.ndx"));
	if (pers)
		maxConf++;

	areas = new AREAs[maxConf];
	if (pers) {
		areas[0].num = -1;
		strcpy(areas[0].numA, "PERS");
		areas[0].name = strdupplus("PERSONAL");
		areas[0].attr = PUBLIC | PRIVATE;
	}
	for (int c = pers; c < maxConf; c++) {
		areas[c].num = atoi(nextLine());		// conf #
		sprintf(areas[c].numA, "%d", areas[c].num);
		areas[c].name = strdupplus(nextLine());		// conf name
		areas[c].attr = PUBLIC | PRIVATE;
	}

	for (int c = 0; c < 3; c++)
		strncpy(textfiles[c], nextLine(), 12);

	fclose(ctrdatFile);
}

/* Read the QWKE file TOREADER.EXT. As yet, this does very little. */

void qwkpack::readToReader()
{
	char *s;
	int cnum, attr;

	if ((ctrdatFile = mm->workList->ftryopen("toreader.ext", "rb"))) {
		while (!feof(ctrdatFile)) {
			s = nextLine();

			if (!strncasecmp(s, "area ", 5)) {
			    if (sscanf(s + 5, "%d %s", &cnum, s) == 2) {

				// If a group is marked subscribed:
				if (strchr(s, 'a') || strchr(s, 'p') ||
				    strchr(s, 'g'))
					attr = ACTIVE;
				else
					attr = 0;

				// "Handles" or "Anonymous":
				if (strchr(s, 'H') || strchr(s, 'A'))
					attr |= ALIAS;

				// Public-only/Private-only:
				if (strchr(s, 'P'))
					attr |= PRIVATE;
				else
					if (strchr(s, 'O'))
						attr |= PUBLIC;
					else
						attr |= (PUBLIC | PRIVATE);

				/* Set character set to Latin-1 for
				   Internet or Usenet areas -- but is this
				   the right thing here?
				*/
				if (strchr(s, 'U') || strchr(s, 'I'))
					attr |= LATINCHAR;

				areas[getXNum(cnum)].attr = attr;
			    }
			} else
				if (!strncasecmp(s, "alias ", 6)) {
					cropesp(s);
					mm->resourceObject->set(AliasName,
						s + 6);
				}
		}
		fclose(ctrdatFile);
	}
}

void qwkpack::initMessagesDat()
{
	if (!(msgdatFile = mm->workList->ftryopen("messages.dat", "rb")))
		fatalError("Could not open MESSAGES.DAT");
	if (!numMsgs)
		numMsgs = mm->workList->getSize() >> 7;
}

// ---------------------------------------------------------------------------
// The QWK reply methods
// ---------------------------------------------------------------------------

qwkreply::qwkreply(mmail *mmA, specific_driver *baseClassA)
{
	mm = mmA;
	baseClass = (qwkpack *) baseClassA;
	init();
}

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

void qwkreply::init()
{
	replyText = 0;
	qwke = baseClass->isQWKE();
	repFileName(baseClass->getBBSID());
	mychdir(mm->resourceObject->get(ReplyDir));
	replyExists = !access(replyPacketName, R_OK | W_OK);

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

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

	while (noOfLetters--) {
		remove(curr->fname);
		next = curr->nextRecord;
		delete curr;
		curr = next;
	}
	delete[] replyText;
}

void qwkreply::repFileName(const char *qFile)
{
	int c;

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

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

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

bool qwkreply::getRep1(FILE *rep, upl_list *l)
{
	FILE *replyFile;
	char *p, *q, *replyText;

	if (!l->qHead.init(rep))
		return false;

	mytmpnam(l->fname);

	if (!(replyFile = fopen(l->fname, "wt")))
		return false;

	replyText = new char[l->qHead.msglen + 1];
	fread(replyText, l->qHead.msglen, 1, rep);

	for (p = &replyText[l->qHead.msglen - 1]; ((*p == ' ') ||
		(*p == (char) 227)) && (p > replyText); p--);
	*++p = '\0';

	for (p = replyText; *p; p++)
		if (*p == (char) 227)
			*p = '\n';	// PI-softcr

	// Get extended (QWKE-type) subject line, if available:

	bool anyfound;
	q = replyText;

	do {
		char *q2;
		anyfound = false;

		q2 = (char *) onecomp((unsigned char *) q,
				l->qHead.subject, "subject:");
		if (q2) {
			q = q2;
			anyfound = true;
		}

	} while (anyfound);

	l->qHead.msglen = p - q;	//strlen(replyText);

	fwrite(q, 1, l->qHead.msglen, replyFile);
	fclose(replyFile);
	delete[] replyText;

	return true;
}

void qwkreply::readRep()
{
	FILE *repFile;
	upl_list baseUplList, *currUplList;
	file_list *upWorkList;
	char repName[13];

	upWorkList = new file_list(mm->resourceObject->get(UpWorkDir));
	sprintf(repName, "%s.msg", findBaseName(replyPacketName));
	if (!(repFile = upWorkList->ftryopen(repName, "rb")))
		fatalError("Error opening REP");
	fseek(repFile, 128, SEEK_SET);

	noOfLetters = 0;

	currUplList = &baseUplList;
	while (!feof(repFile)) {
		currUplList->nextRecord = new upl_list;
		currUplList = currUplList->nextRecord;
		if (!getRep1(repFile, currUplList)) {	// ha eof/error
			delete currUplList;
			break;
		}
		noOfLetters++;
	}
	uplListHead = baseUplList.nextRecord;

	fclose(repFile);
	remove(upWorkList->exists(repName));
	delete upWorkList;
}

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

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

area_header *qwkreply::getNextArea()
{
	return new area_header(mm, 0, "REPLY", "REPLIES",
		"Letters written by you", (qwke ? "QWKE replies" :
		"QWK replies"), (COLLECTION | REPLYAREA | ACTIVE |
		PUBLIC | PRIVATE), noOfLetters, 0, 25);
}

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

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

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

letter_header *qwkreply::getNextLetter()
{
	static net_address nullNet;

	letter_header *newLetter = new letter_header(mm,
			uplListCurrent->qHead.subject,
			uplListCurrent->qHead.to, uplListCurrent->qHead.from,
			uplListCurrent->qHead.date, 0,
			uplListCurrent->qHead.refnum,
			currentLetter, currentLetter,
			baseClass->getXNum(uplListCurrent->qHead.msgnum) + 1,
			uplListCurrent->qHead.privat,
			uplListCurrent->qHead.msglen, this, nullNet);

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

const char *qwkreply::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->qHead.msglen;
		replyText = new char[msglen + 1];
		msglen = fread(replyText, 1, msglen, replyFile);
		fclose(replyFile);
		replyText[msglen] = '\0';
	} else
		replyText = 0;

	return replyText;
}

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

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

int qwkreply::monthval(const char *abbr)
{
	static const char *month_abbr[] =
		{"Jan", "Feb", "Mar", "Apr", "Mar", "Jun",
	 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	int c;

	for (c = 0; c < 12; c++)
		if (!strcmp(month_abbr[c], abbr))
			break;
	return c + 1;
}

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

	strncpy(newList->qHead.subject, newLetter.getSubject(), 71);
	strncpy(newList->qHead.from, newLetter.getFrom(), 25);
	strncpy(newList->qHead.to, newLetter.getTo(), 25);

	newList->qHead.msgnum = atoi(mm->areaList->getShortName());
	newList->qHead.privat = newLetter.getPrivate();
	newList->qHead.refnum = newLetter.getReplyTo();
	strcpy(newList->fname, newLetterFileName);

	time_t tt = time(0);
	strftime(newList->qHead.date, 15, "%m-%d-%y %H:%M",
		localtime(&tt));

	newList->qHead.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 qwkreply::killLetter(int letterNo)
{
	upl_list *actUplList, *tmpUplList;

	if (!noOfLetters || (letterNo < 1) || (letterNo > noOfLetters))
		fatalError("Internal error in qwkreply::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;
	resetLetters();
}

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

void qwkreply::addRep1(FILE *rep, upl_list *l)
{
	FILE *replyFile;
	char *replyText, *p, *lastsp = 0;
	int chunks, length, sublen, count = 0;
	bool longsub;

	l->qHead.output(rep);

	length = l->qHead.msglen;
	sublen = strlen(l->qHead.subject);
	longsub = (sublen > 25);

	if (longsub)
		length += sublen + 11;

	chunks = (length + 127) / 128;
	if (!chunks)
		chunks = 1;

	replyText = new char[chunks * 128 + 1];
	memset(&replyText[(chunks - 1) * 128], ' ', 128);

	p = replyText;

	if (longsub)
		p += sprintf(p, "Subject: %s\n\n", l->qHead.subject);

	replyFile = fopen(l->fname, "rt");	// !! check it !
	fread(p, l->qHead.msglen, 1, replyFile);
	fclose(replyFile);

	replyText[length] = 0;
	for (p = replyText; *p; p++) {
		if (*p == '\n') {
			*p = (char) 227;
			count = 0;
			lastsp = 0;
		} else
			count++;
		if (*p == ' ')
			lastsp = p;
		if ((count >= 80) && lastsp) {
			*lastsp = (char) 227;
			count = p - lastsp;
			lastsp = 0;
		}
	}
	replyText[length] = (char) 227;

	fwrite(replyText, 1, chunks * 128, rep);
	delete[] replyText;
}

void qwkreply::makeReply()
{
	FILE *repFile;
	upl_list *actUplList;
	char repFileName[13], tmp[256];

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

	// Delete old packet
	sprintf(tmp, "%s/%s", mm->resourceObject->get(ReplyDir),
		replyPacketName);
	remove(tmp);

	if (!noOfLetters)
		return;

	sprintf(repFileName, "%s.msg", findBaseName(replyPacketName));
	repFile = fopen(repFileName, "wb");	//!! no check yet

	sprintf(tmp, "%-128s", baseClass->getBBSID());
	fwrite(tmp, 128, 1, repFile);

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

	fclose(repFile);

	//pack the files
	compressAddFile(mm->resourceObject,
		mm->resourceObject->get(ReplyDir),
			replyPacketName, repFileName);

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