/*	Poll.C by Jon Radoff - Extensive modification to work with
 *			       Lattice 5.02 by Russel Miranda
 *
 *	This source code is placed in the public domain.  Feel free to use
 *	it all you like, make a billion dollars by using it in your own
 *	software, etc.
 *
 *
 *-------------------------------------------------------------------------
        STERLING SERVICE NOTE: Use this public domain sourcecode file
        for Paragon BBS as a guide to the proper construction of doors
        for Sterling Service BBS. As long as you are aware of the
        differences between the two BBS programs, you should be 
        able to construct door programs that will run on both Sterling Service
        and Paragon! 
 *-------------------------------------------------------------------------
 *
 *
 *	This should be the examples used for ALL development of "Doors" type
 *	programs for the Paragon BBS.  If you use this as the basis for
 *	any online games, programs, etc. that you write, your software
 *	should be completely upwardly compatible to other versions of
 *	the Paragon BBS system, including future multiline/multiuser versions.
 *
 *	This source code communicates to Paragon via it's "Paragon Door
 *	Protocol."  This gives four advantages over what might be
 *	considered "standard" doors:
 *
 *	1.  Upward compatibility (Your door program will get more powerful
 *	    as Paragon does;  it will also be able to access multiple
 *	    serial ports in future versions, without you having to rewrite
 *	    any of your code).
 *	2.  Reduced code length (since you are accessing functions that
 *	    are within Paragon, you don't need to program a great deal of
 *	    the functions you'd normally have to with doors on other
 *	    software).
 *	3.  Reduced development overhead;  you'll be able to concentrate on
 *	    creating a quality game, program, or utility, rather than having
 *	    to spend a lot of time with Amiga-specific, low-level bologna.
 *	4.  Integration.  The door program will become integrated into the
 *	    BBS.  It will work in the same screen, abiding by the same
 *	    restraints for where 'normal' text may be, the clock is updated,
 *	    stacked commands from the BBS may pass over into the door
 *	    program, etc.
 *
 *
 *		Please note that this is just examples code!  There is a lot
 *	more that can be done with Paragon door protocol than is shown in this
 *	simple poll taking program.  Please read PPD.Doc for a full overview
 *	of the commands that Paragon can accept via message ports.
 *
 *------------------------------------------------------------------------
        STERLING SERVICE NOTE: The complete list of commands available to 
        door programs in Sterling Service is listed in the Appendix of the
        User Manual. Summation of door differences is supplied in the file
        Doors.Doc.
 *------------------------------------------------------------------------
 *
 *	Theory of operation
 *
 *		When a person accesses a "Door" on Paragon, Paragon opens
 *	up an Exec message port, and monitors it for command messages from
 *	any the program.  This port is named depending on the conditions
 *	the program was entered under:  The first part of the name will
 *	always be "DoorControl" which is appended by a number from 0-9,
 *	depending on the line number the user is currently on.  Line 0 is
 *	always local, 1 is always the internal serial port, and lines 2-9
 *	are for additional lines.
 *
 *		The BBS will now run a the door program as a task seperate
 *	from Paragon.   It will be passed one parameter from Paragon:
 *	a number from 0-9, as outlined above.  With this information, the
 *	door program will open up it's reply message port, named "DoorReply"
 *	with this number appended to the name.  The door program will then
 *	look for the correct DoorControl port as well, based upon this
 *	number.  Now it is all ready to go!
 *
 *		Inside this source are contained all the functions for
 *	accessing every Paragon Door Protocol command.  Stick to this
 *	protocol if you want to be upwardly compatible!  It has been well
 *	thought out to allow for future versions (already under development!)
 *	and to protect from using the same port name twice, etc.
 *
 *		Of course, we can't take care of everything.  Remember these
 *	things when you are writing your software:
 *
 *		o ALWAYS check for possible disk i/o errors and handle
 *		  appropriately.  You may want to make it possible for
 *                multiple people to access a door at once...
 *
 *		o Remember that if you want more than one person to be
 *		  able to access it at once, to take care of filing system
 *		  Locks when writing or reading from files.  Always check
 *		  to see if a file is locked by the file system before you
 *		  try to access it;  if it is, try looping back up to
 *		  whatever number of times you think is appropriate (and
 *		  a Delay(10L) could put a nice time delay between each
 *		  access attempt).
 *
 *		o PLEASE test your program;  don't forget to try running
 *		  multiple copies of it at the same to make sure it will
 *		  work alright when several people access it at once.
 *
 *		Don't forget that there is a Paragon Door Protocol interpreter
 *	on the main program disk which will allow you to test the program a
 *	little easier than having to load up one or more copies of the BBS at
 *	once. 
 *
 *--------------------------------------------------------------------------
        STERLING SERVICE NOTE: The above interpreter isn't supplied, as it's
        part of the Paragon BBS package. You'll have to use Sterling BBS to 
        test your door program. Note that the Demo board can be used for this
        purpose by someone who doesn't own Sterling Service, but who wants to
        develop doors for it!
 *--------------------------------------------------------------------------
 *
 */

#if LATTICE
#include <proto/exec.h>
#else
#include <functions.h>
#endif
#include <exec/exec.h>
#include <fcntl.h>
#include <stdio.h>

#define CAR if(doormsg->carrier==1) return;
#define DROP if(doormsg->carrier==1) end();

struct DoorMsg {
	struct Message Door_Msg;
	short command;
	short data;
	char string[80];
	short carrier;
	};

struct DoorMsg *doormsg,*garbage;
struct MsgPort *MyPort,*replyport;

extern void getsvar();
extern void sendmessage();
extern void hotkey();
extern void prompt();
extern void sprompt();
extern void showfile();
extern void end();

struct Task *mytask; /* Pointer to this task so we can name it */

int err;

main(argc,argv)
int argc;
char *argv[];
{
	char username[81],line_number;
	char tmp[81],str[81];
	char doorspath[80];
	char rportname[10],cportname[12];	/* ReplyPort and Control Port names */
	int num,numq,numa;
	short answers[26][21];
	int coun,coun2,inn,tota;
	int polln;
	int fd;
	FILE *fp,*fopen();
	float percent;
	
	/* Pull out the line number from the argv field to find where we are */
	
	line_number=argv[1][0];
	
	mytask=FindTask(0L);
	mytask->tc_Node.ln_Name = "Poller V2.2";    /* You'll probably want to
						     change the name to the
						     name of whatever your door
						     is! */
	
	/* Create the Reply port, with the name based on the number...

	   Attempts at doing this with a simple "sprintf" failed, causing
	   the creation of garbled portnames.  This works fine.  Stick with
           it.  */

	switch(line_number){
		case '0':	replyport=CreatePort("DoorReply0",0L); break;
		case '1':	replyport=CreatePort("DoorReply1",0L); break;
		case '2':	replyport=CreatePort("DoorReply2",0L); break;
		case '3':	replyport=CreatePort("DoorReply3",0L); break;
		case '4':	replyport=CreatePort("DoorReply4",0L); break;
		case '5':	replyport=CreatePort("DoorReply5",0L); break;
		case '6':	replyport=CreatePort("DoorReply6",0L); break;
		case '7':	replyport=CreatePort("DoorReply7",0L); break;
		case '8':	replyport=CreatePort("DoorReply8",0L); break;
		case '9':	replyport=CreatePort("DoorReply9",0L); break;
	}
	
	doormsg=(struct DoorMsg *)AllocMem((long)sizeof(*doormsg),MEMF_PUBLIC);
	
	if(doormsg==0)
	{
		puts("Couldn't allocate DoorMsg!");
		return;
	}
	
	doormsg->Door_Msg.mn_Node.ln_Type = NT_MESSAGE;
	doormsg->Door_Msg.mn_ReplyPort = replyport;
	doormsg->Door_Msg.mn_Length = (UWORD)sizeof(*doormsg);
	
	/* Locate the correct DoorControl port based on the line number */
	
	sprintf(cportname,"DoorControl%c",line_number);
   	MyPort=FindPort(cportname);
   	if(MyPort==0L) 
	{
		puts("DoorControl port not located!");
		FreeMem(doormsg,(long)sizeof(*doormsg));
		return;
	}
	
	/* Now for the main program area - see below for explanations of
	   getsvar, sendmessage, and hotkey (as well as many more). */
	
	getsvar(1,username);
	getsvar(7,doorspath);

	sendmessage("\r\nVoting Booth V2.2\r\n",1);
	
	for(;;)		/* Main loop... repeat forever.	*/
	{
		do
		{
			sendmessage("",1); 
			sprintf(str,"%sPoll/PollMenu.TXT",doorspath);
			showfile(str);
			sendmessage("",1);
			sprompt("Which poll do you wish to take/view (q to exit)? ",str,3);
			DROP;
			if((str[0]=='q')||(str[0]=='Q'))
				end();
			sscanf(str,"%d",&num);
			polln=num;
			sprintf(str,"%sPoll/Poll%d.Dat",doorspath,polln);
		}
		while(!(fp=fopen(str,"r")));
		fclose(fp);
		
		retry1:
		sprintf(str,"%sPoll/Poll%d.Ans",doorspath,polln);
		if( (fd=open(str,O_RDONLY)) !=-1)
		{
			read(fd,(char *)answers,sizeof(answers));
			close(fd);
		}
		else
		{

			/* If we couldn't open the file, was it because
			   of an object in use error?  If so, repeat
                           back (this makes it usable with multiusers */

			err = (int)((long)IoErr()); 
			if(err==202) {              
				Delay(30L);
				goto retry1;
			}
			setmem(&answers[0][0],sizeof(answers),0); /* Zero the array */
		}
		
		if(!(taken(doorspath,polln,username)))
		{
			sprintf(str,"%sPoll/Poll%d.Users",doorspath,polln);
			fp=fopen(str,"a");
			fprintf(fp,"%s\n",username);
			fclose(fp);
			
			sprintf(str,"%sPoll/Poll%d.Dat",doorspath,polln);
			fp=fopen(str,"r");
			fgets(tmp,80,fp);
			sscanf(tmp,"%d",&numq);
			for(coun=1; coun<=numq; coun++)
			{
				sendmessage("",1);
				fgets(tmp,80,fp);
				sendmessage(tmp,1);
				fgets(tmp,80,fp);
				sscanf(tmp,"%d",&numa);
				for(coun2=1; coun2<=numa; coun2++)
				{
					fgets(tmp,80,fp);
					tmp[(strlen(tmp))]=NULL;
					tmp[(strlen(tmp))-1]=NULL;
					sprintf(str," (%d) %s",coun2,tmp);
					sendmessage(str,1);
				}
				do
				{
					prompt("\r\nChoice: ",str,4);
					if(doormsg->carrier==1)
					{
						fclose(fp);
						end();
					};
					sscanf(str,"%d",&inn);
				}
				while((inn>numa)||(inn<1));
				answers[coun][inn]++;
			}
			fclose(fp);
			
			retry2:
			sprintf(str,"%sPoll/Poll%d.Ans",doorspath,polln);
			fd=open(str,O_WRONLY+O_CREAT);

			err = (int)((long)IoErr());
			if(err==202) {
				Delay(30L);
				goto retry2;
			}

			write(fd,(char *)answers,sizeof(answers));	
			close(fd);
		}
		
		prompt("\r\nDo you wish to view the results? ",str,3);
		DROP;	
		if((str[0]=='Y')||(str[0]=='y'))
		{
			tota=0;
			for(coun2=1; coun2!=21; coun2++)
				tota=tota+answers[1][coun2];
			sprintf(tmp,"\r\nThis poll has been taken %d times.\r\n",tota);
			sendmessage(tmp,1);
			sprintf(str,"%sPoll/Poll%d.Dat",doorspath,polln);
			fp=fopen(str,"r");
			fgets(tmp,80,fp);
			sscanf(tmp,"%d",&numq);
			sendmessage("",1);
			for(coun=1; coun<=numq; coun++)
			{
				fgets(tmp,80,fp);
				sendmessage(tmp,1);
				fgets(tmp,80,fp);
				sscanf(tmp,"%d",&numa);
				tota=0;
				for(coun2=1; coun2<=numa; coun2++) 
					tota=tota+answers[coun][coun2];
				for(coun2=1; coun2<=numa; coun2++)
				{
					fgets(tmp,80,fp);
					tmp[(strlen(tmp))]=NULL;
					tmp[(strlen(tmp))-1]=NULL;
					percent=(((float)answers[coun][coun2])/((float)tota))*100;
					sprintf(str," (%5.1f%%) %s",percent,tmp);
					sendmessage(str,1);
				}
				hotkey("\r\nPress any key...",str);
				if(doormsg->carrier==1)
				{
					fclose(fp);
					end();
				}
				sendmessage("\r\n",1);
			}
			sendmessage("",1);
			fclose(fp);
		}
	}
}

/* int taken(path,pollnum,name) - checks to see if user (string "name") has
 *	taken poll number "pollnum".  Also needs doorspath "path".  This
 *	just made the program easier to read.  It's not one of the standard
 *	door protocol functions.
 */



int taken(path,pollnum,name)
char path[25];
int pollnum;
char name[80];
{
	int r=FALSE;
	char user[80];
	char filename[81],tmp2[81];
	FILE *file,*fopen();
	
	(void)strcpy(user,name);
	(void)strcat(user,"\n");
	
	retry1: sprintf(filename,"%sPoll/Poll%d.Users",path,pollnum);
	if(!(file=fopen(filename,"r"))) {
		err = (int)((long)IoErr());
		if(err==202) {
			Delay(30L);
			goto retry1;
		}
		return FALSE;
	}

	do
	{
		fgets(tmp2,80,file);
		if(!(strcmp(tmp2,user)))
		{
			r=TRUE;
			break;
		}
	}
	while(strlen(tmp2)>=3);
	fclose(file);
	return r;
}

/* Never use return or exit to drop out of the program;
	  end() will make sure that the ports are deleted, etc.,
	  and notifies Paragon that the door is done, and that
	  it is time to return to the BBS.

	  If the message port is idle for more than three minutes,
	  however, it assumes the program had a serious failure
	  and will self-abort back to the BBS.

	  "Serious failures" of doors are recorded in Paragon.LOG,
	  and if the count goes over five for a give day and/or
	  period of time the program is online for, it won't let
	  anyone into the doors. */

/*									*
 *	 Here starts the standard Paragon Door Protocol functions.	*
 *									*/
 
 
/*
 * end() -  Notifies Paragon that we are done and ready to continue with
 *		the BBS.  ALWAYS use this to exit from your program, or
 *		the BBS will go to never-never land.
 */

void end()
{
	doormsg->command = 20;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
	FreeMem(doormsg,(long)sizeof(*doormsg));
	DeletePort(replyport);
	exit(0);
}

/*
 * getsvar(typ,mstring) - Gets certain string variables from Paragon.
 * "mstring" is a pointer to a string to dump the string into.  "typ" tells
 * it what you want: 1=Name, 2=Password, 3=Address, 4=City, 5=State
 * 6=Postal code, 7=Door pathname, 8=Default BBS pathname.
 */

void getsvar(typ,mstring)
int typ;
char *mstring;
{
	doormsg->data = typ;
	doormsg->command = 14;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	strcpy(mstring,doormsg->string);
	(void)GetMsg(replyport);
}

/*
 * sendmessage(mstring,nl) - Sends a message to the local window and to
 * the modem (if applicable).  "mstring" is the output string you want to
 * send, "nl" is and integer which is 1 if you want it to send the
 * c/r+l/f combination, or 0 if not.
 */

void sendmessage(mstring,nl)
char mstring[];
int nl;
{
	doormsg->data = nl;
	doormsg->command = 1;
	strcpy(doormsg->string,mstring);
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
}

/*
 * hotkey(mstring,ostring) - outputs the string "mstring" and waits
 * for one key which it will place in element [0] of "ostring".
 * "mstring" may be a null string, in which case it will only wait for
 * the key, and not output any prompt.
 */

void hotkey(mstring,ostring)
char mstring[];
char *ostring;
{
	strcpy(doormsg->string,mstring);
	doormsg->command = 8;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
	strcpy(ostring,doormsg->string);
}

/*
 * prompt(mstring,ostring,len) - outputs the string "mstring" and inputs
 * a string which is placed in ostring.  'len' is the maximum number of
 * characters which will be accepted.
 */

void prompt(mstring,ostring,len)
char mstring[];
char *ostring;
int len;
{
	strcpy(doormsg->string,mstring);
	doormsg->data=len;
	doormsg->command = 6;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
	strcpy(ostring,doormsg->string);
}

/* sprompt is the same as prompt, except it will accept stacked commands
 * (with the '/' key) and will send back any stacked command which is
 * currently in the command buffer instead of waiting for input from the
 * user.  It will even accept stacked commands which bridged over from the
 * BBS.
 */

void sprompt(mstring,ostring,len)
char mstring[];
char *ostring;
int len;
{
	strcpy(doormsg->string,mstring);
	doormsg->data=len;
	doormsg->command = 7;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
	strcpy(ostring,doormsg->string);
}

/* showfile(mstring) shows the text file which mstring is the path to.
   It handles ^C aborting, and ^S/^Q pausing. */

void showfile(mstring)
char mstring[];
{
	strcpy(doormsg->string,mstring);
	doormsg->command = 10;
	PutMsg(MyPort,(struct Message *)doormsg);
	(void)WaitPort(replyport);
	(void)GetMsg(replyport);
}


