/* parsemsg.c
	Auto: smake -f src:bbsread/parsemsg/makefile
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/semaphores.h>
#include <libraries/dos.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <utility/date.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

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

#include	<libraries/bbsread.h>
#include <proto/bbsread.h>

#include <libraries/utgui.h>
#include <proto/utgui.h>

#include <libraries/reqtools.h>
#include <proto/reqtools.h>

#define ULONG_MAX		4294967295
#define BUFLEN			250
#define GRABLEN		100
#define NAMELENGTH	31
#define CSI "\x9b"

/*  Global structure to hold librarybases  */
struct TaskData
{
	struct ExecBase *SysBase;
	struct DosLibrary *DOSBase;
	struct Library *UtilityBase;
	struct Library *BBSReadBase;
	struct Library *UTGuiBase;
	struct BBSListItem *mybbs;
	struct List *conflist;
	struct List *farealist;
	ULONG msgnr;
	ULONG replyto;
	TEXT from[NAMELENGTH];
	TEXT to[NAMELENGTH];
	TEXT confname[NAMELENGTH];
	TEXT subject[NAMELENGTH];
	BOOL private;
	BOOL read;
	ULONG time;
	struct MinList linelist;
	ULONG lines;
	BPTR fh;
	STRPTR errmsg;
	APTR msgpool;
	APTR confpool;
	APTR pbar;
	STRPTR pubscreen;
};

#define SysBase		td->SysBase
#define DOSBase		td->DOSBase
#define UtilityBase	td->UtilityBase
#define BBSReadBase	td->BBSReadBase
#define UTGuiBase		td->UTGuiBase
#define mybbs			td->mybbs
#define conflist		td->conflist
#define farealist		td->farealist
#define msgnr			td->msgnr
#define replyto		td->replyto
#define from			td->from
#define to				td->to
#define confname		td->confname
#define subject		td->subject
#define private		td->private
#define read			td->read
#define time			td->time
#define linelist		td->linelist
#define lines			td->lines
#define fh				td->fh
#define errmsg			td->errmsg
#define msgpool		td->msgpool
#define confpool		td->confpool
#define pbar			td->pbar
#define pubscreen		td->pubscreen


extern STRPTR progname, progdate;
extern LONG version, revision;

/*  Prototypes for external functions  */
extern void * __asm AsmCreatePool(register __d0 ULONG, register __d1 ULONG, register __d2 ULONG, register __a6 struct ExecBase *);
extern void __asm AsmDeletePool(register __a0 void *, register __a6 struct ExecBase *);
extern void * __asm AsmAllocPooled(register __a0 void *, register __d0 ULONG, register __a6 struct ExecBase *);
extern void __asm AsmFreePooled(register __a0 void *, register __a1 void *, register __d0 ULONG, register __a6 struct ExecBase *);

extern APTR SPrintf(STRPTR dest, STRPTR fmtstr, ...);
extern struct Library *OpenBRLibrary(struct ExecBase *, struct DosLibrary *, STRPTR, LONG);

/*  Prototypes for global functions  */
struct Node * FindiName(struct List *list, STRPTR name);
BOOL DoMsgAdding(struct TaskData *td);
BOOL ParseConfList(struct TaskData *td);
BOOL ParseFileList(struct TaskData *td);
STRPTR MyStrTok(STRPTR *tokptr, STRPTR input);
UBYTE *StripAnsi(UBYTE *s);

STRPTR monthnames[] = 
{ 
	"January", "February", "March", 
	"April", "May", "June", 
	"July", "August", "September", 
	"October", "November", "December" 
};

UBYTE *template = "BBSNAME,GRAB,PUBSCREEN/K,ARCHIVE/S,DELETE/S,USAGE/S";

enum {
	TEM_BBSNAME,
	TEM_GRAB,
	TEM_PUBSCREEN,
	TEM_ARCHIVE,
	TEM_DELETE,
	TEM_USAGE,
	TEM_NUMBEROF
};

STRPTR fmtstr =
"%s V%ld.%ld\n"
"Usage: %s [bbsname] [grab] [PubScreen name] [ARCHIVE] [DELETE] [USAGE]\n"
"Copyright © Ultima Thule Software %s  Author: Eivind Nordseth\n%s";

LONG __saveds NoName(void)
{
	struct TaskData TaskData, *td;
	struct GlobalConfig *cfg = NULL;
	struct ConfListItem *myconf;
	struct RDArgs *rdargs = NULL, *args = NULL;
	BOOL newmsg = FALSE, findsubject = FALSE, getlines = FALSE, bufCopyBack = FALSE, addedMsgs = FALSE;
	LONG array[TEM_NUMBEROF], retval = RETURN_FAIL, len, filelen, filepos;
	struct List *bbslist = NULL;
	STRPTR strp, foo, grab = NULL;
	struct MinNode *node;
	TEXT buf[BUFLEN], grabname[GRABLEN], tmpfile[GRABLEN];
	BPTR lock, tmpfh = NULL;
	UWORD charset;
	
	setmem(td = &TaskData, sizeof(struct TaskData), 0);
	SysBase = *(struct ExecBase **)4;

	if(!(DOSBase = (struct DosLibrary *) OpenLibrary(DOSNAME, 0L))) return(10000L);
	if(DOSBase->dl_lib.lib_Version < 37)
	{
		errmsg = "Needs Kick V37+";
		goto quit;
	}
	if(!(UtilityBase = OpenLibrary("utility.library", 37L))) goto quit;

	if(!(BBSReadBase = OpenBRLibrary(SysBase, DOSBase, BBSREADNAME, 3L)))
	{
		errmsg = "Needs bbsread.library V3+";
		goto quit;
	}

	UTGuiBase = OpenBRLibrary(SysBase, DOSBase, "utgui.library", 1L);

	if(!(args = AllocDosObject(DOS_RDARGS, NULL))) goto quit;
	setmem(array, TEM_NUMBEROF * sizeof(LONG), 0);

	SPrintf(args->RDA_ExtHelp = buf, fmtstr, progname, version, revision, 
		progname, progdate, template);

	if(!(rdargs = ReadArgs(template, array, args)))
	{
		errmsg = "Error in arguments.";
		goto quit;
	}

	if(array[TEM_USAGE] || !array[TEM_BBSNAME] || !array[TEM_GRAB]) 
	{
		Printf(fmtstr, progname, version, revision, progname, progdate, "");
		retval = RETURN_OK;
		goto quit;
	}
	
	pubscreen = (STRPTR) array[TEM_PUBSCREEN];

	if(!(cfg = GetGlobalConfig())) { errmsg = "Error geting global config."; goto quit; }

	if(!(ConfigGlobalTags(cfg, CG_BufCopyBack, TRUE, TAG_END))) { errmsg = "Error changing global config."; goto quit; }
	bufCopyBack = TRUE;
		
	if(!(bbslist = GetBBSList())) { errmsg = "Error geting bbslist."; goto quit; }

	if(!(mybbs = (struct BBSListItem *) FindiName(bbslist, (STRPTR) array[TEM_BBSNAME])))
	{
		errmsg = "Unknown BBS";
		goto quit;
	}
	StartOfAdding(mybbs);

	if(mybbs->bl_Data->bd_Flags & BDF_ADD_USERS)
	{
		WriteBRUserTags(mybbs, WBRUSR_OnlyIfNotExist, TRUE, BRUSR_Name, "SYSOP", TAG_END);
	}

	if(!(conflist = GetConfList(mybbs))) { errmsg = "Error geting conflist."; goto quit; }

	if(ParseConfList(td)) goto quit;
	if(ParseFileList(td)) goto quit;

	if(!(confpool = AsmCreatePool(MEMF_ANY, 1024, 512, SysBase)))
	{
		errmsg = "Out of memory";
		goto quit;
	}


/* Make a copy of all confnames in the correct conference charset. */

	myconf = (struct ConfListItem *) &conflist->lh_Head;
	
	while((myconf = (struct ConfListItem *)myconf->cl_Node.ln_Succ)->cl_Node.ln_Succ)
	{
		if((charset = ConfCharset(mybbs, myconf)) != BRCS_ISO) 
		{
			if(!(myconf->cl_Node.ln_Name = AsmAllocPooled(confpool, 
				strlen(myconf->cl_Node.ln_Name) + 1, SysBase))) 
			{
				errmsg = "Out of memory";
				goto quit;
			}
			CharsetConvert(BRCS_ISO, charset, myconf->cl_Data->cd_Name, 
				myconf->cl_Node.ln_Name, 0);
		}
	}

	if(!(lock = Lock((STRPTR) array[TEM_GRAB], ACCESS_READ)))
	{
		errmsg = "No grabfile"; 
		goto done;
	}
	UnLock(lock);

	if(array[TEM_ARCHIVE]) 
	{
		if(pubscreen && UTGuiBase)
		{
			if(!pbar)
			{
				if(!(pbar = ugOpenProgressBar(progname, 
					PB_Total, 1, 
					PB_ScreenName, pubscreen, 
					PB_AbortText, "_Abort",
					PB_ProgressText, "Unarchiving grab",
					PB_ProgressCWidth, 23,
					TAG_END)))
				{
					errmsg = "Coundn't open progress bar";
					goto quit;
				}
			}
			else
			{
				if(ugUpdateProgressBar(pbar,
					PB_Total, 1,
					PB_Current, 0,
					PB_ProgressText, "Unarchiving grab",
					TAG_END)) goto done;
			}

			SPrintf(tmpfile, "t:ParseMsg.%08lx", FindTask(NULL));

			tmpfh = Open(tmpfile, MODE_NEWFILE);
		}
	
		if(UnArchiveTags((STRPTR) array[TEM_GRAB], 
			UA_DestDir, mybbs->bl_BBSPath, 
			tmpfh ? SYS_Output : TAG_IGNORE, tmpfh,
			TAG_END))
		{
			if(tmpfh)
			{
				Seek(tmpfh, 0, OFFSET_BEGINNING);
				
				while(FGets(tmpfh, buf, BUFLEN - 1)) FPuts(Output(), buf);
			}

			errmsg = "Couldn't unarchive archive";
			goto quit;
		}
		
		strncpy(grabname, mybbs->bl_BBSPath, GRABLEN);
		AddPart(grabname, mybbs->bl_Data->bd_GrabName, GRABLEN);

		grab = grabname;
	}
	else 	grab = (STRPTR) array[TEM_GRAB];
	
	if(fh) Close(fh);
	if(!(fh = Open(grab, MODE_OLDFILE)))
	{
		errmsg = "Unable to open grabfile";
		goto quit;
	}
	
	Seek(fh, 0, OFFSET_END);
	if((filelen = Seek(fh, 0, OFFSET_BEGINNING)) == -1)
	{
		errmsg = "Seek error";
		goto quit;
	}
	filepos = 0;

	if(pubscreen && UTGuiBase)
	{
		if(!pbar)
		{
			if(!(pbar = ugOpenProgressBar(progname, 
				PB_Total, filelen, 
				PB_ScreenName, pubscreen, 
				PB_AbortText, "_Abort",
				PB_ProgressText, "Parsing messages",
				PB_ProgressCWidth, 23,
				TAG_END)))
			{
				errmsg = "Coundn't open progress bar";
				goto quit;
			}
		}
		else
		{
			if(ugUpdateProgressBar(pbar,
				PB_Total, filelen,
				PB_Current, filepos,
				PB_ProgressText, "Parsing messages",
				TAG_END)) goto done;
		}
	}
	else PutStr(CSI "0 p\n\n\n\n");

	NewList((struct List *)&linelist);
	if(!(msgpool = AsmCreatePool(MEMF_ANY, 1024, 512, SysBase)))
	{
		errmsg = "Out of memory";
		goto quit;
	}

	while(FGets(fh, buf, BUFLEN - 1))
	{
		filepos += strlen(buf);

		if(findsubject)
		{
			if(!strncmp(buf, "   Entered on ",14))
			{
				struct ClockData cd[1];

				setmem(cd, sizeof(struct ClockData), '\0');

				if(!isdigit(*(strp = &buf[14]))) 
				{
					while(*strp != ' ') if(!*(++strp)) goto datefail; 	/* Spool over ABBS day	*/
					if(*strp) strp++;
				}

				cd->mday = atol(strp);
				while(*strp != ' ') if(!*(++strp)) goto datefail;	/* Spool over date str	*/

				if(!*(foo = ++strp)) goto datefail;

				while(*strp != ' ') if(!*(++strp)) goto datefail;	/* Spool over month str	*/
				if(strp[-1] == ',') strp[-1] = '\0';
				*strp = '\0';

				for(cd->month = 0; cd->month < 12; cd->month++)
					if(!strcmp(foo, monthnames[cd->month])) break;
		
				if(cd->month++ == 12) goto datefail;
				
				if(!*(++strp)) goto datefail;

				cd->year = atol(strp);

				while(*strp != ' ') if(!*(++strp)) goto datefail;	/* Spool over year		*/
				
				if(!strncmp(strp, " at ", 4))
				{
					strp += 4;
					strp[2] = '\0';
					strp[5] = '\0';
					cd->hour = atol(strp);
					cd->min = atol(strp + 3);	/* Calculate time		*/
				}

				time = Date2Amiga(cd);
datefail:
				charset = 0;
			}
			if(!strncmp(buf, "   Reply to msg",15))
			{
				strp = &buf[18];

				while(*strp != '.') if(!*(strp++)) break;
				*strp = '\0';
				replyto = atol(&buf[18]);
			}
			if(!strncmp(buf, "Subject: ",9))
			{
				stccpy(subject, &buf[9], NAMELENGTH);
				if(subject[strlen(subject) - 2] == '\r') subject[strlen(subject) - 2] = '\0';
				findsubject = FALSE;
				getlines = TRUE;
				lines = 0;
				FGets(fh, buf, BUFLEN - 1);
				filepos += strlen(buf);
				FGets(fh, buf, BUFLEN - 1);
				filepos += strlen(buf);
				continue;
			}
		}
		if(newmsg) 
		{
/*			FPuts(Output(), buf);  */
			
			confname[0] = to[0] = from[0] = '\0';
			msgnr = ULONG_MAX;
			private = FALSE;
			read = FALSE;
			time = 0 ;

			strp = buf;
			do 
			{
				if(!*(++strp)) goto line1fail;
				while(*strp != ' ') if(!*(++strp)) goto line1fail;
			} while(strncmp(strp, " message",8));

			*strp = '\0';

			stccpy(confname, buf, NAMELENGTH);

			while(*strp != '#') if(!*(++strp)) goto line1fail;

			if(!*(foo = ++strp)) goto line1fail;
			while(*strp != ' ') if(!*(++strp)) goto line1fail;
			*strp = '\0';
			msgnr = atol(foo);

			while(*strp != ' ') if(!*(++strp)) goto line1fail;
			if(!*(foo = ++strp)) goto line1fail;

			do 
			{
				if(!*(++strp)) goto line1fail;
				while(*strp != ' ') if(!*(++strp)) goto line1fail;
			} while(strncmp(strp, " to ",4));
			*strp = '\0';
			stccpy(from, foo, NAMELENGTH);

			if(strlen(++strp) <= 3) goto line1fail;
			foo = strp + 3;

			*(strp = foo + strlen(foo) - 2) = '\0';

			if(*(--strp) == '.')
			{
				*strp = '\0';
				
				while(*(--strp) == ')')
				{
					while(*(--strp) != '(' && strp > foo);
					if(*(strp) != '(') break;

					if(!stricmp(strp, "(PRIVATE)")) private = TRUE;
					if(!strcmp(strp, "(read)")) read = TRUE; 

					if(read || private) *(--strp) = '\0';
					else break;
				}
			}
			
			stccpy(to, foo, NAMELENGTH);

line1fail:			
			if(!confname[0]) stccpy(confname, "No Conf", NAMELENGTH); 
			if(!to[0])       stccpy(to, "Unknown", NAMELENGTH);
			if(!from[0])     stccpy(from, "Unknown", NAMELENGTH); 

			newmsg = FALSE;
			findsubject = TRUE;
		}

		len = strlen(buf);
		if(buf[len > 2 ? len - 3 : 0] == '\r' && buf[len > 2 ? len - 2 : 1] == '\r') 
		{
			if(msgnr)
			{
				if(DoMsgAdding(td)) 
				{
					errmsg = "Failure when adding message";
					goto quit;
				}
				NewList((struct List *)&linelist);
				AsmDeletePool(msgpool, SysBase);
				if(!(msgpool = AsmCreatePool(MEMF_CLEAR, 1024, 512, SysBase)))
				{
					errmsg = "Out of memory";
					goto quit;
				}
				addedMsgs = TRUE;
			}
			newmsg = TRUE;
			getlines = FALSE;
		}

		if(getlines)
		{
			if(!(node = (struct MinNode *) AsmAllocPooled(msgpool, sizeof(struct MinNode) + 80, SysBase)))
			{
				errmsg = "Out of memory";
				goto quit;
			}
			strp = (STRPTR) &node[1];
			stccpy(strp, buf, 80);
			if(strp[strlen(strp) - 2] == '\r') strp[strlen(strp) - 2] = '\0';
			AddTail((struct List *)&linelist, (struct Node *) node);
			lines++;			
		}
		if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
		{
			errmsg = "***Break";
			goto done;
		}
		if(pbar) if(ugUpdateProgressBar(pbar, PB_Current, filepos, TAG_END)) goto done;
	}
	if(lines)
	{
		if(DoMsgAdding(td)) 
		{
			errmsg = "Failure when adding message";
			goto quit;
		}
		addedMsgs = TRUE;
	}

	if(fh) Close(fh);
	fh = NULL;

	if(!addedMsgs) 
	{
		errmsg = "No messages to add";
		goto quit;
	}

	if(array[TEM_DELETE]) DeleteFile((STRPTR) array[TEM_GRAB]);

done:   /*  All stuff is done  */

	retval = RETURN_OK;

	Flush(Output());

quit:
	if(pbar) ugCloseProgressBar(pbar);
	else Write(Output(), CSI "1 p", 4); 

	if(tmpfh)
	{
		Close(tmpfh);
		DeleteFile(tmpfile);
	}

	if(errmsg)
	{
		struct ReqToolsBase *ReqToolsBase = NULL;

		if(pbar)
		{
			if(ReqToolsBase = (struct ReqToolsBase *) OpenLibrary(REQTOOLSNAME, 38))
			{
				rtEZRequestTags(errmsg, "_OK", NULL, NULL, 
					RT_ReqPos, REQPOS_CENTERSCR,
					RT_PubScrName, pubscreen,
					RTEZ_ReqTitle, progname,
					RT_Underscore, '_',
					TAG_END);

				CloseLibrary((struct Library *) ReqToolsBase);
			}
		}

		if(!ReqToolsBase)
		{
			Write(Output(), errmsg, strlen(errmsg));
			Write(Output(), "\n", 1);
		}
	}
	if(fh) Close(fh);
	if(grab && array[TEM_ARCHIVE]) DeleteFile(grab);
	if(cfg)
	{
		if(bufCopyBack) ConfigGlobalTags(cfg, CG_BufEndCopyBack, TRUE, TAG_END);
		FreeBRObject(cfg);
	}
	if(mybbs) EndOfAdding(mybbs);

	if(bbslist)   FreeBRObject(bbslist);
	if(conflist)  FreeBRObject(conflist);
	if(farealist) FreeBRObject(farealist);

	if(rdargs) FreeArgs(rdargs);
	if(args)   FreeDosObject(DOS_RDARGS, args);

	if(msgpool) AsmDeletePool(msgpool, SysBase);
	if(confpool) AsmDeletePool(confpool, SysBase);
	if(UTGuiBase)   CloseLibrary(UTGuiBase);
	if(BBSReadBase) CloseLibrary(BBSReadBase);
	if(UtilityBase) CloseLibrary(UtilityBase);
	CloseLibrary((struct Library *) DOSBase);
	return(retval);
}


struct Node * FindiName(
struct List *list, 
STRPTR name)
{
	struct Node *node = (struct Node *) &list->lh_Head;
	
	while((node = node->ln_Succ)->ln_Succ)
	{
		if(!stricmp(name, node->ln_Name)) return(node);
	}
	return(NULL);
}

/*  returns error */
BOOL DoMsgAdding(
struct TaskData *td)
{
	struct ConfListItem *myconf;
	ULONG n = 0L;
	STRPTR *linearray;
	struct MinNode *node;
	BOOL error = TRUE;
	UWORD charset;

	if(!lines) return(FALSE);

	if(!(myconf = (struct ConfListItem *) FindiName(conflist, confname)))
	{
/*		Printf("New conference!\n");  */

		if((charset = ConfCharset(mybbs, NULL)) != BRCS_ISO) 
			if(!CharsetConvert(charset, BRCS_ISO, confname, NULL, 0L)) goto fail;

		if(!(myconf = ConfigConfTags(NULL, 
			CC_ConfList, conflist, 
			CC_AddToBBS, mybbs, 
			CC_ConfName, confname, 
			CC_SetConfFlags, CDF_MEMBER_OF,
			TAG_END))) goto fail; 

		if(charset != BRCS_ISO) /* Convert conference name? */
		{
			if(!(myconf->cl_Node.ln_Name = AsmAllocPooled(confpool, 
				strlen(myconf->cl_Node.ln_Name) + 1, SysBase))) 
			{
				errmsg = "Out of memory";
				goto fail;
			}
			CharsetConvert(BRCS_ISO, charset, myconf->cl_Data->cd_Name, 
				myconf->cl_Node.ln_Name, 0);
		}
	} 
	else 
	{
		charset = ConfCharset(mybbs, myconf);

		if(!(myconf->cl_Data->cd_Flags & CDF_MEMBER_OF)) 
			ConfigConfTags(myconf, CC_SetConfFlags, CDF_MEMBER_OF, TAG_END);
	}

	if(!(linearray = AsmAllocPooled(msgpool, (lines + 1) * sizeof(STRPTR), SysBase))) return(TRUE);

	node = (struct MinNode *) &linelist.mlh_Head;
	while((node = node->mln_Succ)->mln_Succ) 
	{
		linearray[n] = (STRPTR) &node[1]; 
		if(charset != BRCS_ISO) CharsetConvert(charset, BRCS_ISO, linearray[n], NULL, 0);
		n++;
/*		Printf("line %ld of %ld:%s\n", n, lines, &node[1]); */
	}
	if(n) { linearray[--n] = NULL; }
	if(n) { if(!strlen(linearray[--n])) linearray[n] = NULL; }

	if(charset != BRCS_ISO)
	{
		CharsetConvert(charset, BRCS_ISO, from, NULL, 0);	
		CharsetConvert(charset, BRCS_ISO, to, NULL, 0);	
		CharsetConvert(charset, BRCS_ISO, subject, NULL, 0);	
	}
	
	if(!WriteBRMessageTags(myconf,
		BRMSG_FromName, from,
		(strcmp(to, "ALL") ? BRMSG_ToName : TAG_IGNORE), to,
		BRMSG_OrginalNr, msgnr,
		(replyto ? BRMSG_RefNr : TAG_IGNORE), replyto,
		(time ? BRMSG_CreationDate : TAG_IGNORE), time,
		BRMSG_Subject, subject,
		BRMSG_Text, linearray,
		WBRMSG_MarkMessage, TRUE,
		WBRMSG_Private, private,
		WBRMSG_Read, read,
		TAG_END)) goto fail;

	if(mybbs->bl_Data->bd_Flags & BDF_ADD_USERS || private)
	{
		WriteBRUserTags(mybbs, WBRUSR_OnlyIfNotExist, TRUE, BRUSR_Name, from, TAG_END);

		if(strcmp(to, "ALL"))
			WriteBRUserTags(mybbs, WBRUSR_OnlyIfNotExist, TRUE, BRUSR_Name, to, TAG_END);
	}
	error = FALSE;

fail:
	if(!pbar || error)
	{
		if(!pbar) PutStr("\x9b\x34\x41");

		Printf("Conf: %-30.s\nMsgnr: %5.ld   Replyto: %5.ld\nFrom: %-30.s To: %-30.s\n", 
			myconf->cl_Data->cd_Name, msgnr, replyto, from, to);
		Printf("Subject: %-60.s\n", subject);
	}
	lines = 0; replyto = 0;
	return(error);
}

/****** parsemsg/ParseConfList ******************************************
*
*   NAME	
*	ParseConfList -- Parses conference list.
*
*   SYNOPSIS
*	error = ParseConfList( td )
*
*	BOOL ParseConfList( struct TaskData * );
*
*   FUNCTION
*	Parses conferences.txt and updates the conference list for a bbs.
*	Dependant of SysBase, DOSBase, UtilityBase, BBSReadBase, UTGuiBase,
*	mybbs, pubscreen, pbar and conflist defined in the TaskData structure.
*
*   INPUTS
*	td - TaskData structure.
*
*   RESULT
*	error - Boolean.
*	    The errmsg field in TaskData may be set to point to an error
*	    message if the function fails.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/
BOOL ParseConfList(
struct TaskData *td)
{
	struct ConfListItem *myconf, *nextconf;
	struct PassConfListItem *passconf;
	struct List list[1];
	STRPTR foo, bits;
	TEXT ct[BUFLEN], fname[GRABLEN];
	BOOL bitdone, biterr, heading = TRUE, error = TRUE, delete = FALSE;
	LONGBITS flags;
	BPTR dofh = NULL;
	APTR pool = NULL;
	LONG filelen, filepos;

	strncpy(fname, mybbs->bl_BBSPath, GRABLEN);
	strncat(fname, "conferences.txt", GRABLEN - strlen(fname));

	if(!(dofh = Open(fname, MODE_OLDFILE))) return(FALSE);
	
	Seek(dofh, 0, OFFSET_END);
	if((filelen = Seek(dofh, 0, OFFSET_BEGINNING)) == -1)
	{
		errmsg = "Seek error";
		goto fail;
	}
	filepos = 0;

	if(pubscreen && UTGuiBase)
	{
		if(!pbar)
		{
			if(!(pbar = ugOpenProgressBar(progname, 
				PB_Total, filelen, 
				PB_ScreenName, pubscreen, 
				PB_AbortText, "_Abort",
				PB_ProgressText, "Parsing conference list",
				PB_ProgressCWidth, 23,
				TAG_END)))
			{
				errmsg = "Coundn't open progress bar";
				goto fail;
			}
		}
		else
		{
			if(ugUpdateProgressBar(pbar,
				PB_Total, filelen,
				PB_Current, filepos,
				PB_ProgressText, "Parsing conference list",
				TAG_END)) goto done;
		}
	}
	else PutStr(CSI"0 p\n");

	NewList(list);
	if(!(pool = AsmCreatePool(MEMF_ANY, 512, 256, SysBase)))
	{
		errmsg = "Out of memory";
		goto fail;
	}

	while(FGets(dofh, ct, BUFLEN - 1))
	{
		filepos += strlen(ct);
		StripAnsi(ct);

		if(heading)
		{
			if(!(strcmp(ct, "-----------------"))) heading = FALSE;
			continue;
		}
		bits = ct;
		while(*bits != ':') if(!*(++bits)) break;
		if(!*bits) continue;

		*bits = '\0';
		foo = bits++ - 1;

		while(foo > ct && *foo == ' ') *(foo--) = '\0';
		if(foo == ct) continue;
		
		bitdone = FALSE;
		biterr = FALSE;
		flags = 0;
		while(!bitdone)
		{
			foo = ++bits;
			
			while(*foo && *foo != '.' && *foo != ',') foo++;
			if(!*foo) 
			{
				biterr = TRUE;
				break;
			}
			if(*foo == '.') bitdone = TRUE;
			*(foo++) = '\0';
			
			if(!strcmp(bits, "MAIL"))            flags |= CDF_MAIL;
			if(!strcmp(bits, "READ ONLY"))       flags |= CDF_READ_ONLY;
			if(!strcmp(bits, "Obligatory"))      flags |= CDF_COMPULSORY;
			if(!strcmp(bits, "FILE INFO"))       flags |= CDF_FILE_INFO;
			if(!strcmp(bits, "USER INFO"))       flags |= CDF_USER_INFO;
			if(!strcmp(bits, "Private allowed")) flags |= CDF_PRIVATE_ALLOWED;
			if(!strcmp(bits, "Member"))          flags |= CDF_MEMBER_OF;
			if(!strcmp(bits, "Alias"))           flags |= CDF_ALIAS | CDF_ENTER_ONLY_TO_ALL;
			bits = foo;
		}
		if(biterr) continue;
		
		if(!(passconf = AsmAllocPooled(pool, sizeof(struct PassConfListItem) + strlen(ct) + 1, SysBase)))
		{
			errmsg = "Out of memory";
			goto fail;
		}
		strcpy(passconf->pl_Name = passconf->pl_Node.ln_Name = (STRPTR) &passconf[1], ct);
		passconf->pl_Description = NULL;
		passconf->pl_BBSConfNr   = -1;
		passconf->pl_Flags       = flags;
		AddTail(list, (struct Node *) passconf);

		if(!pbar)
		{
			PutStr(CSI "A");
			Printf("Conf: %-30.s, Flags: %l08x\n", ct, flags);
		}

		if((myconf = (struct ConfListItem *) FindiName(conflist, ct)) || (flags & CDF_MEMBER_OF))
		{
			if(!(myconf = ConfigConfTags(myconf, CC_ConfList, conflist, 
				CC_AddToBBS, mybbs, CC_ConfName, ct, CC_ClearConfFlags, 
				CDF_MEMBER_OF | CDF_MAIL | CDF_READ_ONLY | CDF_COMPULSORY | CDF_FILE_INFO 
				| CDF_USER_INFO | CDF_PRIVATE_ALLOWED | CDF_ENTER_ONLY_TO_ALL | CDF_ALIAS
				| CDF_NOT_ON_BBS | CDF_REPLY_ONLY_TO_ALL | CDF_NO_LINEWRAP_DISPLAY, 
				CC_SetConfFlags, flags, TAG_END)))
			{
				if(pbar) Printf("Conf: %-30.s, Flags: %l08x\n", ct, flags);
	
				errmsg = "ConfigConf() failed";
				goto fail;
			}
			myconf->cl_UserData = (APTR) TRUE;
		}
		if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
		{
			errmsg = "***Break";
			goto done;
		}
		if(pbar) if(ugUpdateProgressBar(pbar, PB_Current, filepos, TAG_END)) goto done;
	}

	nextconf = (struct ConfListItem *) conflist->lh_Head;

	while(nextconf = (struct ConfListItem *) (myconf = nextconf)->cl_Node.ln_Succ)
	{
		struct ConfInternal *confint = myconf->cl_Internal;

		if(confint->ci_LastMsg < confint->ci_FirstMsg  
			&& (!(myconf->cl_Data->cd_Flags & CDF_MEMBER_OF) || !myconf->cl_UserData))
		{
			ConfigConfTags(myconf, CC_DeleteConf, TRUE, TAG_END);
		}
		else 
		{
			if(!myconf->cl_UserData) 
				ConfigConfTags(myconf, 
					CC_SetConfFlags, CDF_NOT_ON_BBS, 
					CC_ClearConfFlags, CDF_MEMBER_OF, 
					TAG_END);
		}
	}

	if(WritePassiveConfList(mybbs, list))
	{
		errmsg = "WritePassiveConfList() failed";
		goto fail;
	}

	delete = TRUE;
done:
	error = FALSE;
fail:
	if(pool) AsmDeletePool(pool, SysBase);
	if(dofh) Close(dofh);
	if(delete) DeleteFile(fname);

	return(error);
}

/****** parsemsg/ParseFileList ******************************************
*
*   NAME	
*	ParseFileList -- Adds files from newfiles.txt to the file database.
*
*   SYNOPSIS
*	error = ParseFileList( td )
*
*	BOOL ParseFileList( struct TaskData * );
*
*   FUNCTION
*	Parses newfiles.txt and adds new files to the database. Dependant of
*	SysBase, DOSBase, UtilityBase, BBSReadBase, UTGuiBase, mybbs,
*	pubscreen, pbar and farealist defined in the TaskData structure.
*
*   INPUTS
*	td - TaskData structure.
*
*   RESULT
*	error - Boolean.
*	    The errmsg field in TaskData may be set to point to an error
*	    message if the function fails.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/
BOOL ParseFileList(
struct TaskData *td)
{
	struct FAreaListItem *farea = NULL;
	struct SFileResult *found;
	struct ClockData cd[1];
	TEXT ct[BUFLEN], fname[GRABLEN];
	BOOL error = TRUE, back = FALSE;
	STRPTR file, date, kb, min, dlds, descr[2], np;
	BPTR dofh = NULL;
	LONG cnt, filelen, filepos;

	strncpy(fname, mybbs->bl_BBSPath, GRABLEN);
	AddPart(fname, "Newfiles.txt", GRABLEN);

	if(!(dofh = Open(fname, MODE_OLDFILE))) return(FALSE);
	
	Seek(dofh, 0, OFFSET_END);
	if((filelen = Seek(dofh, 0, OFFSET_BEGINNING)) == -1)
	{
		errmsg = "Seek error";
		goto fail;
	}

	if(FGets(dofh, ct, BUFLEN - 1)) 
	{
		if(!strcmp(ct, "DONE\n")) goto done;
	}
	filepos = 5;

	if(pubscreen && UTGuiBase)
	{
		if(!pbar)
		{
			if(!(pbar = ugOpenProgressBar(progname, 
				PB_Total, filelen, 
				PB_ScreenName, pubscreen, 
				PB_AbortText, "_Abort",
				PB_ProgressText, "Parsing newfiles",
				PB_ProgressCWidth, 23,
				TAG_END)))
			{
				errmsg = "Coundn't open progress bar";
				goto fail;
			}
		}
		else
		{
			if(ugUpdateProgressBar(pbar,
				PB_Total, filelen,
				PB_Current, filepos,
				PB_ProgressText, "Parsing newfiles",
				TAG_END)) goto done;
		}
	}
	else PutStr(CSI"0 p\n");

	if(!(farealist = GetFAreaList(mybbs))) 
	{ 
		errmsg = "GetFAreaList() failed";
		goto fail;
	}

	while(FGets(dofh, ct, BUFLEN - 1))
	{
		BOOL notdigit = FALSE;

		filepos += strlen(ct);
		StripAnsi(ct);

		if(ct[0] == '(')
		{
			LONG len = strlen(ct) - 1;
			
			if(ct[len] != ')') continue;

			if(len > 1) 
			{
				ct[len] = '\0';
				
				strcpy(fname, &ct[1]);
				
				if(!(farea = (struct FAreaListItem *) FindiName(farealist, fname)))
				{
					if(!(farea = ConfigFAreaTags(NULL, 
						CFA_FAreaList, farealist,
						CFA_AddToBBS, mybbs,
						CFA_Name, fname, TAG_END))) 
					{ 
						errmsg = "ConfigFArea() failed"; 
						goto fail;
					}
				}
				cnt = 3;		/* Skip farea header */
			}
			else farea = NULL;

			continue;
		}
		if(!farea) continue;
		if(cnt) if(cnt--) continue;
		
		if(strlen(ct) < 40) continue;
		
		if(!(np = MyStrTok(&file, &ct[2])))   continue;
		if(!(np = MyStrTok(&date, np)))       continue;
		if(!(np = MyStrTok(&kb, np)))         continue;
		if(!(np = MyStrTok(&min, np)))        continue;
		if(!(descr[0] = MyStrTok(&dlds, np))) continue;
		descr[1] = NULL;
		
		for(np = date; *np; np++) 
			if(!isdigit(*np)) 
				notdigit = TRUE;

		if(!isdigit(*kb)) notdigit = TRUE;

		if(!isdigit(*min)) notdigit = TRUE;

		for(np = dlds; *np; np++)  
			if(!isdigit(*np)) 
				notdigit = TRUE;

		if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
		{
			errmsg = "***Break";
			goto done;
		}
		if(pbar) if(ugUpdateProgressBar(pbar, PB_Current, filepos, TAG_END)) goto done;

		if(!descr[0] || notdigit) continue;

		if(found = SearchBRFileTags(
			SBRF_SearchFAreaList, farealist,
			SBRF_SearchStr, file,
			SBRF_SearchName, TRUE,
			SBRF_SearchAll, FALSE,
			TAG_END))
		{
			if(found->fr_FilesFound)
			{
				if(found->fr_Files[0].ff_FArea == farea) 
				{
					APTR obj;
					struct TagItem *tags;
					BOOL equal = FALSE;

					if(obj = ReadBRFileTags(found->fr_Files[0].ff_FArea, found->fr_Files[0].ff_FileNr,
						RBRF_FileTagsPtr, &tags, TAG_END))
					{
						if(atol(dlds) == GetTagData(BRFILE_Downloads, 0, tags)) equal = TRUE;
						FreeBRObject(obj);
					}
					if(equal == TRUE) continue;
				}
			
				WriteBRFileTags(farea, 
					WBRF_UpdateFileNr, found->fr_Files[0].ff_FileNr,
					WBRF_DeleteFile, TRUE,
					TAG_END);
			}
			FreeBRObject(found);
		}

		setmem(cd, sizeof(struct ClockData), '\0');
		cd->mday = atol(&date[4]);
		date[4] = '\0';
		cd->month = atol(&date[2]);
		date[2] = '\0';
		cd->year = atol(date);
		if(cd->year < 78) cd->year += 2000;
		else cd->year += 1900;

		time = Date2Amiga(cd);

		if(!pbar)
		{
			if(back) PutStr(CSI "2A"); 
			back = TRUE;

			Printf("Farea: %-20.s File:%-16.s\nDescr: %-40.s\n", 
				farea->al_Data->ad_Name, file, descr[0]);
		}

		if(!WriteBRFileTags(farea, 
			BRFILE_Name, file,
			BRFILE_Date, time, 
			BRFILE_Size, atol(kb) * 1024,
			BRFILE_Downloads, atol(dlds),
			BRFILE_Description, descr, TAG_END))
		{ 
			if(pbar) Printf("Farea: %-20.s File:%-16.s\nDescr: %-40.s\n", 
							farea->al_Data->ad_Name, file, descr[0]);

			errmsg = "WriteBRFile() failed"; 
			goto fail;
		}
	}

	Seek(dofh, 0 , OFFSET_BEGINNING);
	FPuts(dofh, "DONE\n"); 

done:
	error = FALSE;
fail:
	if(dofh) Close(dofh);
	
	return(error);
}

STRPTR MyStrTok(
STRPTR *tokptr,
STRPTR input)
{
	*tokptr = NULL;
	if(!input) return(NULL);

	while(*input && *input == ' ') input++;
	if(!*input) return(NULL);

	*tokptr = input;
	while(*input && *input != ' ') input++;

	if(!*input) return(NULL);

	*input = '\0';
	
	return(input + 1);
}

UBYTE *StripAnsi(UBYTE *s)
{
	STRPTR dest = s, d = s;
	
	while(*s)	
	{
		if((*s == 0x1b) || (*s == 0x9b))
		{
			if(*s == 0x1b)
			{
				if(*++s != '[') continue;
			}
			s++;
			while(*s && *s < '@') s++;
		}
		else if(*s >= 32) *d++ = *s;
		s++;
	}
	*d = '\0';

	return(dest);
}
