/**************************************************************************/
/**                                                                      **/
/**               WWW Fleamarket CGI query server                        **/
/**                                                                      **/
/**               (c) 1994 Takoyaki Software Ltd.                        **/
/**                                                                      **/
/**                        ------------                                  **/
/**                                                                      **/
/**  Idea, programming, and magic are the personal copyright of          **/
/**  Dylan Cuthbert (dylan@takoyaki.demon.co.uk)                         **/
/**                                                                      **/
/**************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>


/* include the following define to generate a moderated version **/

/*#define MODERATED*/

#define VERSION "v0.92"

/** Allow up to 10000 entries in a form (a little over the top maybe?) **/

#define MAX_ENTRIES 10000

#define STOPCHAR 0x07

/** if an entry can't be found return an empty string with this macro **/

#define entry(num,label) (((num)==-1)?"":entries[(num)].label)

/** structure for form entries received from the client **/

typedef struct {
    char *name;
    char *val;
} entry;

/** routines in util.c (public domain) **/

char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);

entry entries[MAX_ENTRIES];
int max=0;
int uniquefileid=0;
int newfleamode;

char wordbuf[10240];

int firstrun = 1;

DIR *dirfd;


/** Define the list of topics                         **/
/** Eventually this should be read from a config file **/

char *topiclist[]=
	{
	"Personal",
	"Leisure",
	"Seeking",
	"Selling",
	"Jobs",
	"Accommodation",
	"Lessons",
	NULL
	};

char topicbuf[256];

char fd_topicname[256];
char fd_classified[10240];
char fd_email[256];
char fd_date[256];

#define FLEAPAGESIZE 10

/*******************************************************************/
/** Open a filename with a unique .extension (1-999)              **/
/** Note: this routine loops forever if there are 998 files       **/
/** This problem will be fixed for the next version               **/

FILE *fopenuniquename(char *fn)
	{
	FILE *fp;
	char *e = strchr(fn,'.')+1;

	while(1)
		{
		sprintf(e,"%d",uniquefileid);
		if (++uniquefileid>999) uniquefileid = 0;
		if (access(fn,F_OK)) if (fp = fopen(fn,"w")) break;
		}

	return fp;
	}

/*******************************************************************/
/** This steps through each file in turn the directory specified  **/
/** this can only be called for one directory, or you need to     **/
/** re-initialize 'firstrun' to 1 (my lazy programming :-)  )     **/

char *GetNextFile(char *dir)
	{
	int done;
	char *n;
	struct dirent *de;
	static char buf[128];

/** First time so actually open the directory **/

	if (firstrun)
		{
		dirfd = opendir(dir);
		firstrun = 0;
		}

/** Loop until end of directory or suitable file **/

	while (1)
		{
		if (de = readdir(dirfd))
			{
			if (strnicmp(de->d_name,"flea.",5)) continue;
			sprintf(buf,"%s/%s",dir,de->d_name);
			return buf;
			}
		else
			{
			closedir(dirfd);
			return NULL;
			}
		}
	}
		



/**********************************************/
/** Search through the FORM contents for a   **/
/** label of the specified name              **/

int findname(char *s)
	{
	int n;

	for (n=0;n<max;n++)
		if (stricmp(entries[n].name,s)==0) return n;

	return -1;
	}

/************************************************/
/** remove a flea from the specified directory **/

void delflea(char *dir,int flea)
	{
	char temp[50];

	sprintf(temp,"%s/flea.%d",dir,flea);
	remove(temp);
	}


/**********************************************/
/** Write a flea out to a file               **/

void fwriteflea(char *dir,char *email,char *classified,char *topic)
	{
	char newfleafile[30];
	FILE *fp;
	char buf[100];
	time_t tim;

	strcpy(newfleafile,dir);
	strcpy(newfleafile+strlen(dir),"/flea.xxx");

/* we must impose a limit on the size of these files in case there is */
/* someone stupid out there who is out to wreck the system            */

	if (email)
		{
		if (strlen(email)>128) email[128] = '\0';
		}
	else email="No e-mail address specified.";

	if (topic)
		{
		if (strlen(topic)>128) topic[128] = '\0';
		}
	else topic="Personal";

	if (classified)
		{
		if (strlen(classified)>1024) classified[1024] = '\0';
		}
	else classified="Nothing specified.";

/* now open the file and write the data out */

	if (fp = fopenuniquename(newfleafile))
		{
		fprintf(fp,"EMAIL %c%s%c\n",STOPCHAR,email,STOPCHAR);
		fprintf(fp,"CLASSIFIED %c%s%c\n",STOPCHAR,classified,STOPCHAR);
		fprintf(fp,"TOPIC %c%s%c\n",STOPCHAR,topic,STOPCHAR);

		time(&tim);
		strftime(buf,100,"%y/%m/%d (%A)",localtime(&tim));
		fprintf(fp,"DATE %c%s%c\n",STOPCHAR,buf,STOPCHAR);
		fclose(fp);
		}
	}

/*************************************************/
/** get a line of text from the file            **/
/** the line is separated by special characters **/
/** defined as STOPCHAR                         **/

char *fgetline(FILE *fp)
	{
	char *w=wordbuf;

	while(!feof(fp) && fgetc(fp)!=STOPCHAR)
		;

	while(!feof(fp) && (w-wordbuf)<10240-1)
		{
		*w++ = fgetc(fp);
		if (w[-1] == STOPCHAR)
			{
			w[-1] = '\0';
			break;
			}
		}
	w[0] = '\0';
	return wordbuf;
	}

/**********************************************/
/** get a word from the file                 **/

char *fgetword(FILE *fp)
	{
	char *w=wordbuf;
	char c;

	while(!feof(fp))
		{
		c = fgetc(fp);
		if (isalpha(c)) break;
		}

	if (isalpha(c)) *w++ = c;

	while(!feof(fp))
		{
		*w++ = fgetc(fp);
		if (w[-1] == ' ')
			{
			w[-1] = '\0';
			break;
			}
		}
	*w = '\0';
	return wordbuf;
	}

/**********************************************/
/** make a HTML selectable list of topics    **/

char *maketopiclist(int id,char *topicname)
	{
	int n=0;

	if (id != -1) sprintf(topicbuf,"<SELECT SIZE=5 NAME=F_TOPIC%d>",id);
	else sprintf(topicbuf,"<SELECT SIZE=5 NAME=NF_TOPIC>",id);

	while (topiclist[n])
		{
		sprintf(topicbuf+strlen(topicbuf),"<OPTION%s>%s\n"
			,(stricmp(topicname,topiclist[n])==0)?" SELECTED":""
			,topiclist[n]);
		n++;
		}

	sprintf(topicbuf+strlen(topicbuf),"</SELECT>");
	return topicbuf;
	}

/**********************************************/
/** get the flea data from the file          **/

void getfleadata(char *s)
	{
	FILE *fp;
	char *w;
	int count=0;

/* set defaults */

	strcpy(fd_email,"No data.");
	strcpy(fd_topicname,"No data.");
	strcpy(fd_classified,"No data.");
	strcpy(fd_date,"No date set.");

	if (fp = fopen(s,"r"))
		{

/* get four items before exitting */

		while (count<4 && !feof(fp))
			{

/* get the data type name */

			w = fgetword(fp);

			if (stricmp(w,"TOPIC")==0)
				{
				strcpy(fd_topicname,fgetline(fp));
				count++;
				}
			if (stricmp(w,"CLASSIFIED")==0)
				{
				strcpy(fd_classified,fgetline(fp));
				count++;
				}
			if (stricmp(w,"EMAIL")==0)
				{
				strcpy(fd_email,fgetline(fp));
				count++;
				}
			if (stricmp(w,"DATE")==0)
				{
				strcpy(fd_date,fgetline(fp));
				count++;
				}
			}

		fclose(fp);
		}

	}
/***************************************************/
/** this deals with the viewing of the fleas      **/
/** it has three modes (depending on newfleamode) **/
/** 0 = normal public viewing                     **/
/** 1 = adminstrator page (current fleas)         **/
/** 2 = adminstrator page (newly submitted fleas) **/
/** note: mode 2 is only relevant if MODERATED is **/
/** defined.                                      **/

void showflealist(int start,char *topic, int newfleamode)
	{
	char *s;
	int id,skip=start,count=0,n;
	char temp[128];
	char temp2[128];

/* display the page title */

	printf("<H1>Kansai-WWW Fleamarket</H1>\n");

/* if the first flea is not the actual first   */
/* we have to display a link to previous fleas */

	if (start>0)
		{

/* generate the topic (if specified) for the link */

		if (topic) sprintf(temp,"TOPIC=%s&",topic);
 		else temp[0] = '\0';

/* set the sysop mode for the link */

		if (newfleamode) sprintf(temp2,"SYSOP=%s&",(newfleamode==1)?"old":"new");
		else temp2[0] = '\0';

/* display the link */

		printf("<HR>--- <A HREF=\"fleamark?START=%d&%s%s\">Previous Fleas</A> ---"
			,(start-FLEAPAGESIZE<0)?0:start-FLEAPAGESIZE,temp,temp2);
		}

/* if adminstrator mode is enabled set up the form */

	if (newfleamode)
		{
		printf("<H2>ADMINISTRATOR MODE ENABLED</H2>");
		printf("<FORM ACTION=\"fleamark\" METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\">");

		if (newfleamode==1) printf("<H2>Editing Current Fleas</H2>");
		else printf("<H2>Editing new submissions</H2>");

#ifndef MODERATED
	printf("<i>This version of Fleamarket is unmoderated.</i>\n");
#endif

		}
	else
		{

/* not in administrator mode so display the topic list */
/* so the user can concentrate on certain topics       */

		printf("<HR>Click on one of the below to concentrate on specific types of fleas.<BR><UL>");
		n=0;
		while (topiclist[n])
			{
			temp[0]='\0';
			if (topic) { if (stricmp(topic,topiclist[n])==0) strcpy(temp,"(currently selected)"); }
			printf("<LI><A HREF=\"fleamark?topic=%s\">%s</A> %s",topiclist[n],topiclist[n],temp);
			n++;
			}
		
		printf("</UL>");

		if (!topic) printf("Currently you are browsing through all types.");
		else printf("Click <A HREF=\"fleamark?start=0\">here</A> to browse through all types at the same time.");

		}

	printf("<HR><H2>FLEAS</H2>");

/* Now cycle through each flea and display info about them */

	while (s = GetNextFile((newfleamode==2)?"newfleas":"fleas"))
		{
		getfleadata(s);

/* skip any fleas that don't match the current topic */

		if (topic) if (stricmp(topic,fd_topicname)!=0) continue;
		if (skip-->0) continue;

		id = strtol(strchr(s,'.')+1,NULL,10);

		printf("<HR>");

/* if admin mode then display FORM data */

		if (newfleamode)
			{
			printf("%d.",start-skip);
			printf("<TEXTAREA ROWS=5 COLS=40 NAME=F_CLASSIFIED%d>%s</TEXTAREA>",id,fd_classified);
			printf("%s<BR>",maketopiclist(id,fd_topicname));
			printf("Email: <INPUT TYPE=TEXT SIZE=40 VALUE=\"%s\" NAME=F_EMAIL%d><BR><i>Last modified: %s</i><P>",fd_email,id,fd_date);
			printf("Function: Add/Update <INPUT TYPE=radio NAME=F_FUNC%d VALUE=add> Delete <INPUT TYPE=radio NAME=F_FUNC%d VALUE=del> Ignore <INPUT TYPE=radio NAME=F_FUNC%d VALUE=ignore CHECKED><BR>",id,id,id);
			}
		else
			{

/* if not admin mode just display <PRE> formatted text */

			sprintf(temp,"(Topic: %s)",fd_topicname);

			printf("%d. %s<PRE>%s</PRE>",start-skip,temp,fd_classified);

			printf("<BR><ADDRESS>--- %s (%s)</ADDRESS>",fd_email,fd_date);

			}

/* have we filled the page yet? */

		if (++count>=FLEAPAGESIZE)
			{

/* generate the topic string (if selected) */

			if (topic) sprintf(temp,"TOPIC=%s&",topic);
			else temp[0] = '\0';

/* generate the sysop mode (if needed) */

			if (newfleamode) sprintf(temp2,"SYSOP=%s&",(newfleamode==1)?"old":"new");
			else temp2[0] = '\0';

/* Display the 'next fleas' link (with the above info in it) */

			printf("<HR>--- <A HREF=\"fleamark?START=%d&%s%s\">Next Fleas</A> ---",start+count,temp,temp2);
			break;
			}

		}

/* if no fleas were displayed, say so */

	if (!count) printf("<HR>No Fleas found.\n");
	else if (newfleamode)
		{

/* fleas were displayed and this is an admin page       */
/* so display the admin password box and submit buttons */

		printf("<HR>Administrator's password: <INPUT TYPE=password NAME=%s><BR>"
			,(newfleamode==1)?"F_PASSWORD":"NF_PASSWORD");
		printf("<HR><INPUT TYPE=submit> <INPUT TYPE=reset>");
		}

	printf("<HR>");

/* close the FORM if admin mode */

	if (newfleamode)
		printf("</FORM>");
	else
		{

/* add links to allow user to add fleas or view admin page */

		printf("Click <A HREF=\"fleamark?start=-1\"><i>here</i></A> to submit your own 'flea' for the Kansai-WWW Fleamarket, ");
		printf("or <A HREF=\"fleamark?sysop=old\"><i>here</i></A> to enter the <i>adminstrator's</i> page.<HR>");
		}


	printf("<H4>DISCLAIMER</H4>");
	printf("The contents of the Kansai-WWW Fleamarket are entirely the responsibility of the ");
	printf("individual 'flea' authors themselves.  Kansai-Net, Kansai-WWW and Takoyaki Software ");
	printf("<i>cannot</i> take any responsibility for what is posted on these pages. ");
	printf("However we retain the right to censor or delete 'fleas' at will.<HR>");

	printf("<i>Fleamarket query server %s</i> (c) 1994 Takoyaki Software Ltd.",VERSION);
	}

/**********************************************/
/* display the FORM to add a new flea         */

void newfleaform()
	{

	printf("<H1>Flea Submission Form</H1>");

	printf("Please fill in the details as correctly as possible, this ");
	printf("will speed up the processing of your entry.");

	printf("<FORM ACTION=\"fleamark\" METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\">");

	printf("<H2>Entry:</H2>");
	printf("<TEXTAREA ROWS=5 COLS=40 NAME=NF_CLASSIFIED></TEXTAREA>");
	printf("%s<BR>",maketopiclist(-1,"personal"));
	printf("Email: <INPUT TYPE=TEXT SIZE=40 NAME=NF_EMAIL><BR>");

	printf("<HR><INPUT TYPE=submit> <INPUT TYPE=reset>");

	printf("</FORM>");

	printf("<HR>Click <A HREF=\"fleamark?start=0\">here</A> to return to Fleamarket.");


	}

/**********************************************/
/* the starting point of life as we know it   */

main(int argc, char *argv[]) {
	register int x,m=0,n;
	int cl,id;
	FILE *fp;
	char temp[40];
	int topic,classified,email;
	int deleted, added, ignored;
	int start;
	char *topicname=NULL;
	char *cl2;

/* this is a header to say we are going to give Mosaic a html doc */

	printf("Content-type: text/html%c%c",10,10);

/*********************************************************/
/**  Deal with GET requests                             **/
/**                                                     **/
/**  There are 3 cases:                                 **/
/**  1. Show a list of fleas                            **/
/**     NAMES: START, TOPIC                             **/
/**  2. Show a list of new fleas with edit options      **/
/**     NAMES: START, TOPIC, SYSOP=old                  **/
/**  3. Show a list of fleas with edit options          **/
/**     NAMES: START, TOPIC, SYSOP=new                  **/
/*********************************************************/

/* Check if its a GET or a POST request */

	if (strcmp(getenv("REQUEST_METHOD"),"POST"))
		{
/* if a GET request the data is sent as an environment variable */

		cl2 = getenv("QUERY_STRING");
		if(cl2 == NULL) {
			start = 0;
		}
		else
			{

/* pull the data out of the environment variable */

			cl2 = strdup(cl2);
			for(x=0;cl2[0] != '\0';x++) {
				m=x;
				entries[x].val = makeword(cl2,'&');
				plustospace(entries[x].val);
				unescape_url(entries[x].val);
				entries[x].name = makeword(entries[x].val,'=');
				}
			max = m+1;
			}

/* get the flea number to start viewing from */
/* this is NOT the filename id               */
/* it just tells showflealist how many fleas */
/* to skip over before displaying            */

		if ((n = findname("START")) != -1) start = strtol(entry(n,val),NULL,10);

/* if start is -1 then display the new flea entry form */

		if (start==-1)
			{
			newfleaform();
			return;
			}

/* get the topic name to focus on (if specified) */

		if ((n = findname("TOPIC")) != -1) topicname = strdup(entry(n,val));

/* sysop mode or not? */

		if ((n = findname("SYSOP")) != -1)
			{
			if (stricmp(entry(n,val),"old") == 0) newfleamode = 1;
			else newfleamode = 2;
			}
		else newfleamode = 0;

/* pow.. display the list */

		showflealist(start,topicname,newfleamode);

		return;
		}

/* now to deal with METHOD=POST type requests */

/* make sure the content type is readable by us */

	if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) {
		printf("<HR>There has been an error in the sending of the data.<BR>");
		printf("Please go back to the page and re-send.<HR>");
		printf("If the problem persists contact: <ADDRESS>dylan@takoyaki.demon.co.uk</ADDRESS>");
		exit(1);
		}

/* get the amount of data sent */

	cl = atoi(getenv("CONTENT_LENGTH"));

/* read in each item */

	for(x=0;cl && (!feof(stdin));x++) {
		m=x;
		entries[x].val = fmakeword(stdin,'&',&cl);
		plustospace(entries[x].val);
		unescape_url(entries[x].val);
		entries[x].name = makeword(entries[x].val,'=');
		}
	max = m+1;

/**********************************************************************/
/*  METHOD=POST data manipulation                                     */
/*                                                                    */
/*  Now we've got the data, we have to work out what it was           */
/*  There are three cases                                             */
/*  1. An item to be added into 'newfleas' ('fleas' if unmoderated)   */
/*     NAMES: NF_EMAIL, NF_CLASSIFIED, NF_TOPIC                       */
/*  2. A list of newfleas to be moved or deleted                      */
/*     NAMES: NF_PASSWORD, F_CLASxxx, F_TOPICxxx (xxx = number)       */
/*            F_FUNCxxx, F_EMAILxxx                                   */
/*  3. A list of fleas to be deleted or updated                       */
/*     NAMES: F_PASSWORD, F_FUNCxxx, F_TOPICxxx, F_CLASxxx, F_EMAILxx */
/**********************************************************************/

/* case #1 requires MF_EMAIL to be present */

	if ((n = findname("NF_EMAIL")) != -1)
		{
		printf("<H1>Fleamarket Data Entry</H1><HR>\n");
		printf("Thankyou for sending us your entry.<P>");

#ifdef MODERATED
		fwriteflea("newfleas",entry(n,val),entry(findname("NF_CLASSIFIED"),val),entry(findname("NF_TOPIC"),val));
		printf("Your data will be processed within a few days and ");
		printf("added to the fleamarket if considered in order.<HR>");
#else
		fwriteflea("fleas",entry(n,val),entry(findname("NF_CLASSIFIED"),val),entry(findname("NF_TOPIC"),val));
		printf("<A HREF=\"fleamark?topic=%s\">",entry(findname("NF_TOPIC"),val));
		printf("Your data is immediately viewable from this link.</A>");
#endif

		printf("<HR><i>Fleamarket query server %s</i> (c) 1994 Takoyaki Software Ltd.",VERSION);

		return;
		}

/* case #2 requires NF_PASSWORD to be present  */
/* case #3 requires F_PASSWORD to be present   */
/* the following routing deals with both cases */
/* as they are almost the same                 */

	if ((findname("NF_PASSWORD") != -1) || (findname("F_PASSWORD") != -1))
		{

		if ((n = findname("NF_PASSWORD")) != -1)
			newfleamode = 1;
		else
			{
			n = findname("F_PASSWORD");
			newfleamode = 0;
			}

/* password should be taken from config file in future version */
/* but hey, I'm not paid to do this you know :-)               */

		if (strcmp(entry(n,val),"whisper"))
			{
			printf("Error: password did not match, data not submitted.\n");
			return;
			}

/* clear the counts */

		deleted = 0; added = 0; ignored = 0;

/* go through each entry in the submitted form */

		for (x=0;x<m;x++)
			{

/* if the entry is F_FUNCxxx where xxx is the file id for the flea */
/* then process it                                                 */

			if (strnicmp(entry(x,name),"F_FUNC",6) == 0)
				{

/* get the id from the 'xxx' part */

				id = strtol(entry(x,name)+6,NULL,10);

/* search for the advert body data (made unique with the id) */

				sprintf(temp,"F_CLASSIFIED%d",id);
				classified = findname(temp);

/* search for the topic name */

				sprintf(temp,"F_TOPIC%d",id);
				topic = findname(temp);

/* search for the email address */

				sprintf(temp,"F_EMAIL%d",id);
				email = findname(temp);

				ignored++;

/* if the function is to delete, then delete */

				if (stricmp(entry(x,val),"del") == 0)
					{
					delflea((newfleamode)?"newfleas":"fleas",id);
					deleted++;
					ignored--;
					}

/* if the function is to add, then add */

				if (stricmp(entry(x,val),"add") == 0)
					{
					fwriteflea("fleas",entry(email,val),entry(classified,val),entry(topic,val));
					delflea((newfleamode)?"newfleas":"fleas",id);
					added++;
					ignored--;
					}

/* if the function is anything else then ignore */

				}
			}


		printf("<HR>%d fleas added/modified.<BR>%d fleas deleted<HR>\n",added,deleted);

		return;
		}
}

/* LE FIN */

