/*
 *	Name:				file.c
 *
 * Description:	File related functions for xferq.library
 *
 * Copyright:		1992-1993 by David Jones.
 *
 * Distribution:
 *		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		David Jones
 *				675 Mass Ave							6730 Tooney Drive
 *				Cambridge, MA							Orleans, Ontario
 *				02139										K1C 6R4
 *				USA										Canada
 *
 *	Usenet:	gnu@prep.ai.mit.edu					dej@qpoint.ocunix.on.ca
 *	Fidonet:												1:163/109.8
 *
 *		$Log: $
 *
 */

#include <exec/types.h>
#include <execlists.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <string.h>
#include "xferq.h"
#include "xferqint.h"
#include "xferq_pragmas.h"

extern BOOL QueueScanned;
extern char *DefaultDomain, *DefaultZone;

char *FidoName = "_$(DOMAIN:S).$(ZONE:D).$(NET:D).$(NODE:D).$(POINT:D).queue";
char *UucpName = "_UUCP.$(UUCPNAME:S).queue";
char *TextName = "_TEXT.$(TEXTNAME:S).queue";
BOOL ConfigRead;

char *MakeFileName(struct NetAddress *addr)
/*
 *	In:	addr				Address to make filename for
 *
 *	Does:	Constructs the filename for a queue file given the address.
 *
 */
{
struct VarList *vl;
char *template, *filename;
#ifdef DEBUG
	int count = GetCount(addr);
#endif

	switch (addr->type) {
	case XQNT_FIDO:
		template = FidoName;
		break;
	case XQNT_UUCP:
		template = UucpName;
		break;
	case XQNT_TEXT:
		template = TextName;
		break;
	default:
		return NULL;
	}
	vl = NewVarList();
	if (!vl) {
		return NULL;
	}
	if (ExamObjectVarList(addr, vl)) {
		filename = MakeString(NULL, template, vl);
	}
	else {
		filename = NULL;
	}
	DropVarList(vl);
#ifdef DEBUG
	CheckCount("MakeFileName", addr, count);
#endif
	return filename;
}


ULONG __saveds __asm LIBNextSeq(void)
/*
 *	Does:	Returns a unique 32-bit sequence number each time the function
 *			is called.  The numbers will repeat after 2^32 calls.
 */
{

	return 0;
}


ULONG TruncateFile(char *name)
/*
 *	In:	name				Name of file to truncate
 *
 * Does:	The file is given zero length, although it still exists.
 *			The function returns the appropriate XfqRemoveWork() code.
 */
{
BPTR fh;

	fh = Open(name, MODE_OLDFILE);
	if (fh) {
		SetFileSize(fh, OFFSET_BEGINNING, 0);
		Close(fh);
		return XQRM_TRUNCATED;
	}
	else {
		Error(XQERROR_DOS);
		return XQRM_ERROR;
	}
}


char *FullPath(char *name)
/*
 *	In:	name				Name to take full path of
 *
 * Does:	Returns a string object which is the fully-qualified path name
 *			of the input.
 */
{
char buf[258];
int i, len;
BPTR lock;
BOOL rc;

	len = strlen(name);
	for (i = 0; i < len; i++) {
		if (name[i] == ':') {
			return CopyString(name);
		}
	}
	lock = Lock(name, ACCESS_READ);
	if (lock) {
		rc = NameFromLock(lock, buf, sizeof(buf));
		UnLock(lock);
		if (rc) {
			return CopyString(buf);
		}
		else {
			Error(XQERROR_DOS);
			return NULL;
		}
	}
	Error(XQERROR_DOS);
	return NULL;
}


BOOL ReadQueueContents(BPTR file, struct SiteNode *sn)
/*
*	In:	file			File containing contents of queue.
*			sn				Site node to read into.
*
*	Does:	Reads the contents of the file, forming the entries for the
*			site queue.
*/
{
char line[260];
struct WorkNode *wn;
long n, len;
char *cp;

	while (FGets(file, line, sizeof(line))) {
		if (IoErr()) {
			Error(XQERROR_DOS);
			DropSiteNode(sn);
			return FALSE;
		}
		wn = XfqCreateObject(XQO_WORKNODE, NULL);
		if (!wn) {
			DropSiteNode(sn);
			return FALSE;
		}
		while (line[0] != '#') {
			switch (line[0]) {
			case 'F':
				len = strlen(line) - 2;	/* Drop header and newline */
				wn->node.ln_Name = CopyStringN(&line[1], len);
				if (!wn->node.ln_Name) {
					XfqDropObject(wn);
					DropSiteNode(sn);
					return FALSE;
				}
				break;
			case 'A':
				len = strlen(line) - 2;	/* Drop header and newline */
				wn->asname = CopyStringN(&line[1], len);
				if (!wn->asname) {
					XfqDropObject(wn);
					DropSiteNode(sn);
					return FALSE;
				}
				break;
			case 'P':
				cp = &line[1];
				GetNumber(cp, 10, 0, &n);
				wn->node.ln_Pri = n;
				break;
			case 'C':
				cp = &line[1];
				GetNumber(cp, 10, 0, &n);
				wn->userFlags = n;
				break;
			case 'S':
				cp = &line[1];
				GetNumber(cp, 10, 0, &n);
				wn->status = n;
				break;
			}
			FGets(file, line, sizeof(line));
			if (IoErr()) {
				Error(XQERROR_DOS);
				DropSiteNode(sn);
				return FALSE;
			}
		} /* while != '#' */
		/*
		 *	Can't use AddWork() cuz that will recurse!
		 */
		XfqCopyObject(sn->site);
		wn->site = sn;
		wn->addr = sn->site;
		wn->sysFlags = XQ_INQUEUE;
		ADDHEAD(&sn->workList, wn);
	} /* while not EOF */
	sn->flags &= ~XQSITE_UNREAD;
	return TRUE;
}


void ReadQueue(struct SiteNode *sn)
/*
 *	In:	sn					Site node to read queue for
 *
 * Does:	Reads the queue file from disk.
 *
 */
{
char *name;
BPTR cdLock, oldLock;
BPTR file;
long *errp;

	cdLock = Lock("XFERQ:", SHARED_LOCK);
	if (!cdLock) {
		Error(XQERROR_DOS);
		return;
	}
	oldLock = CurrentDir(cdLock);
	name = MakeFileName(sn->site);
	if (name) {
		errp = PeekError();
		file = Open(&name[1], MODE_OLDFILE);
		if (file) {
			ReadQueueContents(file, sn);
			Close(file);
			if (!*errp) {
				/* Delete _file if file exists. */
				DeleteFile(name);
			}
		}
		else {
			if (*errp == XQERROR_DOS) {
				Error(XQERROR_OK);	/* clear error condition due to file not
												found which is OK here. */
				file = Open(name, MODE_OLDFILE);
				if (file) {
					ReadQueueContents(file, sn);
					Close(file);
					if (!*errp) {
						Rename(name, &name[1]);
					}
				}
				else {
					if (*errp == XQERROR_DOS) {
						Error(XQERROR_OK);
					}
				}
			}
		}
	}
	XfqDropObject(name);
	CurrentDir(oldLock);
	UnLock(cdLock);
}


BOOL WriteQueue(struct ExtSessWalk *esw)
/*
 *	In:	addr				Address to write queue for
 *
 * Does:	Writes the queue file to disk.
 *
 */
{
char *name;
BPTR fh;
BPTR cdLock, oldLock;
struct WorkNode *wn;
struct SiteNode *sn;

	sn = esw->sn;
	if (!(sn->flags & XQSITE_DIRTY)) {
		return TRUE;
	}
	cdLock = Lock("XFERQ:", SHARED_LOCK);
	if (!cdLock) {
		Error(XQERROR_DOS);
		return FALSE;
	}
	name = MakeFileName(sn->site);
	if (!name) {
		return FALSE;
	}
	oldLock = CurrentDir(cdLock);
	wn = FIRST(&sn->workList);
	if (!NEXT(wn)) {
		DeleteFile(&name[1]);
		sn->flags &= ~XQSITE_DIRTY;
	}
	else {
		fh = Open(name, MODE_NEWFILE);
		if (fh) {
			while (NEXT(wn)) {
				/*
				 *		If IMMEDIATE is in effect, then anything that can cause
				 *		the queue to be read in also nukes any sessions that
				 *		were up so don't even bother to write these out.
				 *
				 *		Similar logic goes for SENDLATER, but this time we
				 *		must clear the SENDLATER bit.
				 */
				if (!(wn->userFlags & XQ_IMMEDIATE)) {
					FPrintf(fh, "F%s\n", wn->node.ln_Name);
					if (wn->asname) {
						FPrintf(fh, "A%s\n", wn->asname);
					}
					FPrintf(fh, "P%ld\n", wn->node.ln_Pri);
					FPrintf(fh, "C%ld\n", wn->userFlags & ~XQ_SENDLATER);
					FPrintf(fh, "S%ld\n", wn->status);
					FPrintf(fh, "#\n", NULL);
				}
				wn = NEXT(wn);
			}
			sn->flags &= ~XQSITE_DIRTY;
			Close(fh);
			DeleteFile(&name[1]);
			Rename(name, &name[1]);
		}
		else {
			Error(XQERROR_DOS);
		}
	}
	CurrentDir(oldLock);
	UnLock(cdLock);
	return TRUE;
}


void ScanQueue(void)
/*
 *	Does:	Scans the queue directory for queue files.  For each queue file
 *			found, it creates a site node and marks it as unread.
 */
{
BPTR cdLock, oldLock;
struct NetAddress *na;
struct SiteNode *sn;
struct AnchorPath *ap;
struct VarList *vl;
long error, type;

	cdLock = Lock("XFERQ:", SHARED_LOCK);
	if (!cdLock) {
		Error(XQERROR_DOS);
		return;
	}
	oldLock = CurrentDir(cdLock);
	ap = AllocMemory(sizeof(struct AnchorPath));
	vl = NewVarList();
	if (ap && vl) {
		ap->ap_Strlen = 0;
		ap->ap_BreakBits = 0;
		ap->ap_Flags = 0;
		error = MatchFirst("#?.queue", ap);
		while (!error) {
			if (ScanString(ap->ap_Info.fib_FileName, &FidoName[1], vl)) {
				type = XQNT_FIDO;
			}
			else if (ScanString(ap->ap_Info.fib_FileName, &UucpName[1], vl)) {
				type = XQNT_UUCP;
			}
			else if (ScanString(ap->ap_Info.fib_FileName, &TextName[1], vl)) {
				type = XQNT_TEXT;
			}
			else {
				type = 0;	/* unknown type - ignore */
			}
			if (type) {
				na = XfqCreateObjectTags(XQO_ADDRESS,
					XQ_Network, type,
					TAG_DONE);
				if (na) {
					na = ModifyObjectVarList(na, vl);
				}
				EmptyVarList(vl);
				if (na) {
					sn = NewSiteQueue(na);
					XfqDropObject(na);
				}
				if (!na || !sn) {
					DropQueue();
					break;
				}
				else {
					sn->flags = XQSITE_UNREAD;
				}
			}
			error = MatchNext(ap);
		}
		switch (error) {
		case ERROR_NO_MORE_ENTRIES:
			QueueScanned = TRUE;
			break;
		case 0:
			Error(XQERROR_NOMEM);
			break;
		default:
			Error(XQERROR_DOS);
			break;
		}
		MatchEnd(ap);
	}
	CurrentDir(oldLock);
	if (ap) {
		FreeMemory(ap);
	}
	if (vl) {
		DropVarList(vl);
	}
	UnLock(cdLock);
}


void ReadConfig(void)
/*
 *	Does:	Reads the configuration file.  At the moment, the file name
 *			is XFERQ:hostaddr and contains the default domain and zone in
 *			the form domain#zone.
 */
{
BPTR fh;
char line[40];
struct NetAddress *na;

	na = NULL;
	fh = Open("XFERQ:hostaddr", MODE_OLDFILE);
	if (fh) {
		if (FGets(fh, line, sizeof(line))) {
			line[strlen(line) - 1] = '\0';
			na = XfqGetAddressTags(line, NULL,
				XQ_Mandatory, XQADDR_DOMAIN | XQADDR_ZONE,
				XQ_Optional, XQADDR_FIDO,
				TAG_DONE);
		}
		Close(fh);
	}
	if (na) {
		XfqDropObject(DefaultDomain);
		DefaultDomain = XfqCopyObject(na->addr.fido.domain);
		XfqDropObject(DefaultZone);
		DefaultZone = XfqCopyObject(na->addr.fido.zone);
		XfqDropObject(na);
	}
	ConfigRead = TRUE;
}

