/* ScriptServ.c
	Auto: smake -f src:bbsread/ScriptServ/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/exall.h>
#include <utility/tagitem.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

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

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

#define USESTRLEN				400L
#define SIZE_EXALLBUFFER	256L
#define CONFIG_VERSION		4L

/*  Global structure to hold librarybases  */
struct TaskData
{
	struct ExecBase *SysBase;
	struct DosLibrary *DOSBase;
	struct Library *BBSReadBase;
	struct Library *UtilityBase;
};

#define SysBase		td->SysBase
#define DOSBase		td->DOSBase
#define BBSReadBase	td->BBSReadBase
#define UtilityBase	td->UtilityBase

extern char *progname, *progdate;
extern LONG *version, *revision;

/*  Prototypes for external functions  */
extern APTR SPrintf(STRPTR dest, STRPTR fmtstr, ...);
extern struct Library *OpenBRLibrary(struct ExecBase *, struct DosLibrary *, LONG);

/*  Prototypes for global functions  */
struct Node * FindiName(struct List *list, STRPTR name);
UBYTE *StripAnsi(UBYTE *s);

UBYTE *template = "BBSNAME,EVENT=EV/N/K/M,DONE/K,ERROR/K,WRITECFG/S,PACKAGEDONE/S,RENAMEGRAB/S,PRIVUPLOADFILE/K,UPDATEFILENOTES/K,USAGE/S";

enum {
	TEM_BBSNAME,
	TEM_EVENT,
	TEM_DONE,
	TEM_ERROR,
	TEM_WRITECFG,
	TEM_PACKAGEDONE,
	TEM_RENAMEGRAB,
	TEM_PRIVUPLOADFILE,
	TEM_UPDATEFILENOTES,
	TEM_USAGE,
	TEM_NUMBEROF
};

/*
   Format of configuration file:
VERSION        (4)
BBSID
BBSTYPE
BBSDIR
POSTCONF
UPLOADDIR
USERNAME
USERSTREET
USERADDRESS
USERCOUNTRY
USERPHONE
NEWFILES			(y/n)
AUTOPRIVDNL		(y/n)
AUTOLOGOFF		(y/n)
USECOLORS		(y/n)
ANSIMENUES		(y/n)
BULLETINS		(y/n)
REPLYPACKET
*/

/*
   Format of event file:
EVENTNR		<- Use when marking done/error
EVENT
<tags in same order as in type config>
*/

STRPTR fmtstr =
"%s V%ld.%ld\n"
"Usage: %s [bbsname] [DONE eventnr] [ERROR eventnr] [WRITECFG]\n"
"          [PACKAGEDONE] [RENAMEGRAB] [PrivUploadFile file]\n"
"          [UpdateFileNotes file] [USAGE]\n"
"Copyright © Ultima Thule Software %s  Author: Eivind Nordseth\n%s";

LONG __saveds NoName(void)
{
	struct TaskData TaskData, *td;
	struct RDArgs *rdargs = NULL, *args = NULL;
	STRPTR errmsg = NULL;
	LONG array[TEM_NUMBEROF], retval = RETURN_FAIL, n;
	struct TypeListItem *mytype = NULL;
	struct BBSListItem *mybbs;
	struct ConfListItem *myconf;
	struct BBSData *bbsdata;
	struct BBSInternal *bbsint;
	struct UserData *userd = NULL;
	struct List *bbslist = NULL, *conflist = NULL;
	struct GlobalConfig *globalcfg = NULL;
	struct ExAllControl *eac = NULL;
	struct ExAllData *ead, *buffer = NULL;
	STRPTR tobuf = NULL, confbuf = NULL, uplpath;
	APTR eveobj = NULL;
	ULONG eventnr, tobuflen = 0, confbuflen = 0;
	TEXT filename[USESTRLEN];
	BPTR fh = NULL, lock = NULL;
	BYTE endchr;

	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", 0L))) return(10000L);

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

 	setmem(array, sizeof(ULONG) * TEM_NUMBEROF, '\0'); 

	if(!(args = AllocDosObject(DOS_RDARGS, NULL))) goto quit;

	SPrintf(args->RDA_ExtHelp = filename, fmtstr, progname, version, revision, 
		progname, progdate, template);
		
/*	Printf("BufUsage: %ld\n", strlen(filename)); */

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

	if(array[TEM_USAGE] || !array[TEM_BBSNAME]) 
	{
		Printf(fmtstr, progname, version, revision, progname, progdate, "");
		retval = RETURN_OK;
		goto quit;
	}
	
	if(!(globalcfg = GetGlobalConfig())) { errmsg = "Error geting global configuration."; goto quit; }

	if(!(bbslist = GetBBSList())) { errmsg = "Error geting bbslist."; goto quit; }

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

	if(!(mytype = TypeFromBBS(mybbs))) { errmsg =  "Error geting type."; goto quit; }

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

	bbsdata = mybbs->bl_Data;
	bbsint  = mybbs->bl_Internal;

	if(array[TEM_DONE])
	{
		STRPTR ptr = (STRPTR) array[TEM_DONE];
		ULONG flags;
		APTR obj;

		eventnr = atol(ptr);
		
		while(isdigit(*ptr)) ptr++;

		if(!*ptr)
		{
			if(obj = ReadBREventTags(mybbs, eventnr, RBREV_Flags, &flags, TAG_END))
			{
				if(!(flags & EDF_ERROR)) UpdateBREventTags(mybbs, eventnr, UBRE_SetDone, TRUE, TAG_END);
				FreeBRObject(obj);
			}
		}
	}

	if(array[TEM_ERROR])
	{
		eventnr = atol((STRPTR) array[TEM_ERROR]);

		UpdateBREventTags(mybbs, eventnr, UBRE_SetError, TRUE, TAG_END);
	}
	
	if(array[TEM_WRITECFG])
	{
		myconf = (struct ConfListItem *) &conflist->lh_Head;

		SPrintf(filename, "t:%s.CONFIG", bbsdata->bd_Name); 
		if(!(fh = Open(filename, MODE_NEWFILE))) 
		{
			errmsg = "Unable to create config file";
			goto quit;
		}
		FPrintf(fh, "%ld\n%s\n%s\n%s\n",
			CONFIG_VERSION,
			bbsdata->bd_GrabName,
			mytype->tl_Data->td_TypeName,
			mybbs->bl_BBSPath);
		
		while((myconf = (struct ConfListItem *) myconf->cl_Node.ln_Succ)->cl_Node.ln_Succ)
		{
			if(myconf->cl_Data->cd_Flags & CDF_MAIL) break;
		}
		if(!myconf->cl_Node.ln_Succ)
		{
			if(myconf = (struct ConfListItem *) conflist->lh_Head)
			{
				if(myconf->cl_Node.ln_Succ) myconf = (struct ConfListItem *) myconf->cl_Node.ln_Succ;
			}
		}
		if(myconf->cl_Node.ln_Succ) FPuts(fh, myconf->cl_Data->cd_Name); 
		else FPuts(fh, "Post");
		FPutC(fh,'\n');

		if(!(uplpath = bbsdata->bd_UploadPath)) 
			if(!(uplpath = globalcfg->gc_UploadPath)) uplpath = "ram:";
		
		FPuts(fh, uplpath);
		endchr = uplpath[strlen(uplpath) - 1];

		if(!(endchr == ':' || endchr == '/')) FPutC(fh, '/');

		if(!(userd = BBSUserData(globalcfg, mybbs)))
		{
			errmsg = "Unable to get BBS User Data";
			goto quit;
		}

		FPrintf(fh, "\n%s\n%s\n%s\n%s\n%s\n",
			userd->ud_Name,
			userd->ud_Street,
			userd->ud_Address,
			userd->ud_Country,
			userd->ud_Phone);

		FPrintf(fh, "%lc\n%lc\n%lc\n%lc\n%lc\n%lc\n", 
			bbsdata->bd_ScrFlags & SF_NEWFILES   ? 'y' : 'n',
			bbsdata->bd_ScrFlags & SF_AUTOPDNL   ? 'y' : 'n',
			bbsdata->bd_ScrFlags & SF_AUTOLOGOFF ? 'y' : 'n',
			bbsdata->bd_ScrFlags & SF_USECOLORS  ? 'y' : 'n',
			bbsdata->bd_ScrFlags & SF_ANSIMENUES ? 'y' : 'n',
			bbsdata->bd_ScrFlags & SF_BULLETINS  ? 'y' : 'n');

		FPrintf(fh, "%s\n", bbsdata->bd_ReplyPacket ? bbsdata->bd_ReplyPacket : (STRPTR) "");

		Close(fh); fh = NULL;
	}

	if(array[TEM_PACKAGEDONE])
	{
		for(n = bbsint->bi_FirstEvent; n <= bbsint->bi_LastEvent; n++)
		{
			ULONG flags;
			APTR obj;

			if(obj = ReadBREventTags(mybbs, n, RBREV_Flags, &flags, TAG_END))
			{
				if(flags & EDF_PACKED) UpdateBREventTags(mybbs, n, UBRE_SetDone, TRUE, TAG_END);
				FreeBRObject(obj);
			}
		}
		PackDataFileTags(PD_EventData, mybbs, TAG_DONE);
	}
	
	if(array[TEM_RENAMEGRAB])
	{
		STRPTR ptr;
		BOOL more, match;

		if(!(lock = Lock(globalcfg->gc_DnloadPath, ACCESS_READ))) goto quit;
		if(!(buffer  = AllocMem(SIZE_EXALLBUFFER, MEMF_ANY))) goto quit;
		if(!(eac = AllocDosObject(DOS_EXALLCONTROL, NULL))) goto quit;
		
		eac->eac_LastKey = (ULONG) (eac->eac_MatchString = (UBYTE *) (eac->eac_MatchFunc = NULL));
		
		do
		{
			more = ExAll(lock, buffer, SIZE_EXALLBUFFER, ED_TYPE, eac);
			if((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES)) goto quit;

			if(eac->eac_Entries == 0) continue;

			for(ead = buffer; ead; ead = ead->ed_Next)
			{
				if(ead->ed_Type > 0) continue;

				match = !strnicmp(bbsdata->bd_GrabName, ead->ed_Name, strlen(bbsdata->bd_GrabName));
				ptr = &ead->ed_Name[strlen(bbsdata->bd_GrabName)];

				if(match && (*ptr == '.' || *ptr == '\0'))
				{
					BPTR dlock, exist;
					LONG num = 0;
					
					dlock = CurrentDir(lock);
					do
					{
						num++;
						SPrintf(filename, "%s%ld%s", bbsdata->bd_GrabName, num, ptr);
						exist = Lock(filename, ACCESS_READ);
						if(exist) UnLock(exist);
					} while(exist);
					
					if(!Rename(ead->ed_Name, filename)) Printf("Unable to rename grab\n");
					CurrentDir(dlock);
					more = FALSE;
					break;
				}
			}
		} while(more);
		UnLock(lock); lock = NULL;
	}
	
	if(array[TEM_PRIVUPLOADFILE])
	{
		BOOL gotpriv = FALSE;

		if(!(fh = Open((STRPTR) array[TEM_PRIVUPLOADFILE], MODE_OLDFILE)))
		{
			errmsg = "Unable to open privuploadfile";
			goto quit;
		}

		while(FGets(fh, filename, USESTRLEN - 1))
		{
			STRPTR ptr = filename, end;

			if(!gotpriv)
			{
				gotpriv = !strncmp(filename, "The following", 13);
				continue;
			}

			if(!*ptr || *ptr == '\n') break;
	
			while(*ptr && *ptr == ' ') ptr++;
			end = ptr;
			while(*end && *end != ' ') end++;
			*(end++) = '\0';

			while(*end && *end != '(') end++;
			if(!(*end)) break;
			
			while(*end && *end != ',') end++;
			if(!(*end)) break;
			
			while(*end && *end != ')') end++;
			if(!(*end)) break;

			Printf("%s\n",ptr);
		}
		Close(fh); fh = NULL;
	}
	
	if(array[TEM_UPDATEFILENOTES])
	{
		if(!(fh = Open((STRPTR) array[TEM_UPDATEFILENOTES], MODE_OLDFILE)))
		{
			errmsg = "Unable to open updatefilenotesfile";
			goto quit;
		}

		if(lock = Lock(bbsdata->bd_DnloadPath ? bbsdata->bd_DnloadPath : globalcfg->gc_DnloadPath, ACCESS_READ))
		{
			lock = CurrentDir(lock);

			while(FGets(fh, filename, USESTRLEN - 1))
			{
				STRPTR ptr;

				StripAnsi(filename);
			
				if(strlen(filename) <= 40) continue;

				for(ptr = &filename[2]; *ptr && *ptr != ' '; ptr++);
				*ptr = '\0';

				if(*(ptr = &filename[39]) == ' ') ptr++;

				if(filename[2]) SetComment(&filename[2], ptr);
			}
			
			UnLock(CurrentDir(lock)); lock = NULL;
		}
	
	}
	if(array[TEM_EVENT])
	{
		ULONG **ptr = (ULONG **) array[TEM_EVENT];
		ULONG eventmask = 0;
	
		while(*ptr)
		{
			ULONG eventnr = **(ptr++);

			if(eventnr < EVE_MAXEVENT) eventmask |= 1 << eventnr;
		}

		SPrintf(filename, "t:%s.EVENTS", bbsdata->bd_Name); 
		if(fh) Close(fh);
		fh = NULL;

		for(n = bbsint->bi_FirstEvent; n <= bbsint->bi_LastEvent; n++)
		{
			struct EventConfigItem *eventcfg;
			struct TagItem *tagitem, *eventtags;
			STRPTR ptr, conf = NULL, nextconf = NULL, msgfile;
			ULONG *tagptr, eventtype;
			APTR obj;
			LONGBITS flags;
			BOOL multiple = FALSE;
			TEXT filebuf[37];

			if(!(obj = ReadBREventTags(mybbs, n, 
				RBREV_EventTagsPtr, &eventtags,
				RBREV_EventType, &eventtype, 
				RBREV_Flags, &flags, TAG_END))) continue;

			if((flags & (EDF_DELETED | EDF_PACKED | EDF_ERROR | EDF_DONE | EDF_FREEZE)) || !((1<<eventtype) & eventmask))
			{
				FreeBRObject(obj);
				continue;
			}
		
			if(!fh)
			{
				if(!(fh = Open(filename, MODE_NEWFILE))) 
				{
					errmsg = "Unable to create event file";
					goto quit;
				}
			}

			eventcfg = (struct EventConfigItem *) &mytype->tl_Data->td_AvailEvents->mlh_Head;

			while((eventcfg = (struct EventConfigItem *) eventcfg->ec_Node.mln_Succ)->ec_Node.mln_Succ)
			{
				if(eventcfg->ec_Event == eventtype) break;
			}
			if(!eventcfg->ec_Node.mln_Succ) { errmsg = "Event type panic"; goto quit; }

			if(ptr = (STRPTR) GetTagData(BREV_Conference, NULL, eventtags))
			{
				if(strchr(ptr, ','))
				{
					ULONG len = strlen(ptr) + 1;

					multiple = TRUE;
					if(len > confbuflen)
					{
						if(confbuf) FreeMem(confbuf, confbuflen);
						if(!(confbuf = AllocMem(confbuflen = len, MEMF_ANY))) goto quit;
					}
					strcpy(conf = confbuf, ptr);
				}
				else conf = ptr;
			} 

			msgfile = (STRPTR) GetTagData(BREV_MsgFile, NULL, eventtags);

			if(msgfile && (GetTagData(BREV_PGPSignID, 0L, eventtags) || GetTagData(BREV_PGPEncryptID, 0L, eventtags)))
			{
				strcpy(filebuf, msgfile);
				strcat(msgfile = filebuf, ".asc");
			}
			
			do
			{
				STRPTR to = NULL, nextto = NULL;

				if(conf) if(nextconf = strchr(conf, ',')) *(nextconf++) = '\0';

				if(ptr = (STRPTR) GetTagData(BREV_ToName, NULL, eventtags))
				{
					if(strchr(ptr, ','))
					{
						ULONG len = strlen(ptr) + 1;

						multiple = TRUE;
						if(len > tobuflen)
						{
							if(tobuf) FreeMem(tobuf, tobuflen);
							if(!(tobuf = AllocMem(tobuflen = len, MEMF_ANY))) goto quit;
						}
						strcpy(to = tobuf, ptr);
					}
					else to = ptr;
				}

				do
				{
					if(to) if(nextto = strchr(to, ',')) *(nextto++) = '\0';

					FPrintf(fh, "%ld%s\n%ld\n", n, multiple ? "M" : "", eventtype);

					if(tagptr = eventcfg->ec_NeedTags)
					{
						while(*tagptr)
						{
							if(!((*tagptr == BREV_PGPSignID) || (*tagptr == BREV_PGPEncryptID)))
							{
								if(tagitem = FindTagItem(*tagptr, eventtags))
								{
									if(*tagptr == BREV_Conference) FPuts(fh, conf);
									else if(*tagptr == BREV_ToName) FPuts(fh, to);
									else if(*tagptr == BREV_MsgFile) FPuts(fh, msgfile);
									else if(*tagptr == BREV_Date)
									{
										struct ClockData cd[1];
								
										Amiga2Date(tagitem->ti_Data, cd);
									
										FPrintf(fh, "%02ld%02ld%02ld", cd->year % 100, cd->month, cd->mday);
									}
									else if(*tagptr & BR_TagString) FPuts(fh, (STRPTR) tagitem->ti_Data);
									else FPrintf(fh, "%ld", tagitem->ti_Data);
								}
								FPutC(fh, '\n');
							}
							tagptr++;
						}
					}

					if(tagptr = eventcfg->ec_OptTags)
					{
						while(*tagptr)
						{
							if(!((*tagptr == BREV_PGPSignID) || (*tagptr == BREV_PGPEncryptID)))
							{
								if(tagitem = FindTagItem(*tagptr, eventtags))
								{
									if(*tagptr == BREV_Conference) FPuts(fh, conf);
									else if(*tagptr == BREV_ToName) FPuts(fh, to);
									else if(*tagptr == BREV_MsgFile) FPuts(fh, msgfile);
									else if(*tagptr == BREV_Date)
									{
										struct ClockData cd[1];
								
										Amiga2Date(tagitem->ti_Data, cd);
									
										FPrintf(fh, "%02ld%02ld%02ld", cd->year % 100, cd->month, cd->mday);
									}
									else if(*tagptr & BR_TagString) FPuts(fh, (STRPTR) tagitem->ti_Data);
									else FPrintf(fh, "%ld", tagitem->ti_Data);
								}
								FPutC(fh, '\n');
							}
							tagptr++;
						}
					}
				} while(to = nextto);
			} while(conf = nextconf);

			if(multiple) FPrintf(fh, "%ld\n%ld\n", n, 7769);

			FreeBRObject(obj);
		}

		if(!fh) DeleteFile(filename);
	}

done:   /*  All stuff is done  */

	retval = RETURN_OK;

	Flush(Output());

quit:
	if(errmsg)
	{
		Write(Output(), errmsg, strlen(errmsg));
		Write(Output(), "\n", 1);
	}
	if(eveobj) FreeBRObject(eveobj);
	if(userd) FreeBRObject(userd);
	if(mytype) FreeBRObject(mytype);
	if(conflist) FreeBRObject(conflist);
	if(bbslist) FreeBRObject(bbslist);
	if(globalcfg) FreeBRObject(globalcfg);

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

	if(confbuf) FreeMem(confbuf, confbuflen);
	if(tobuf)   FreeMem(tobuf, tobuflen);

	if(fh) Close(fh);
	if(lock) UnLock(lock);
	if(eac) FreeDosObject(DOS_EXALLCONTROL, eac);
	if(buffer) FreeMem(buffer, SIZE_EXALLBUFFER);
	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);
}

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);
}
