/**************************************************************************/
/**                                                                      **/
/**              WWW HTML remote authoring system                        **/
/**                                                                      **/
/**               (c) 1994 Takoyaki Software Ltd.                        **/
/**                                                                      **/
/**                        ------------                                  **/
/**                                                                      **/
/**  Idea, programming, and magic are the personal copyright of          **/
/**  Dylan Cuthbert (dylan@takoyaki.demon.co.uk)                         **/
/**                                                                      **/
/**************************************************************************/
/* __PASSLOCK=wombat                                                      */
/**************************************************************************/
/* Version modifications                                                  */
/* v0.5 - first version                                                   */
/* v0.6 - appends last-modified date at bottom of file                    */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#ifdef __GO32__
#include <osfcn.h>
#endif


#define VERSION "v0.6"

#define CONFIGFILE "/home/vinz/httpd_1.3/conf/author.conf"

#define WWWPAGES "/home/vinz/www"

#define MODDATEPREFIX "<HR><I>Author Last Modified: "

/** 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;

char wordbuf[10240];


/**********************************************/
/** 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;
	}


/**********************************************/
void copyfile(char *backupname,char *fn)
	{
	FILE *out,*in;

	if (out = fopen(backupname,"w"))
		{
		if (in = fopen(fn,"r"))
			{

			while (!feof(in))
				fputc(fgetc(in),out);

			fclose(in);
			}
		fclose(out);
		}
	}

/**********************************************/
char *makebackupname(char *fn)
	{
	static char backupname[128];

	if (strlen(fn)>100) fn[100]='\0';

	strcpy(backupname,fn);

	if (strrchr(backupname,'.'))
		strcpy(strrchr(backupname,'.')+1,"bak");
	else strcpy(backupname+strlen(backupname),".bak");

	return backupname;
	}

/**********************************************/
void makebackup(char *fn)
	{

	copyfile(makebackupname(fn),fn);
	}

/**********************************************/
void revert(char *fn,char *pass)
	{
	printf("<H1>Revert To Backup</H1>");
	if (checkpass(fn,pass))
		{
		copyfile(fn,makebackupname(fn));
		printf("<HR>File reverted to backup version.<HR>");
		printf("<FORM ACTION=author METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\">\n");
		printf("<INPUT TYPE=hidden VALUE=%s NAME=OPENFILE>",fn);
		printf("<INPUT TYPE=hidden VALUE=%s NAME=PASSWORD>",pass);
		printf("<INPUT TYPE=submit VALUE=\"Editor\">\n");
		printf("</FORM>");
		}
	else
		{
		printf("<HR>Error: Password did not match.  Go back and try again.<HR>");
		}
	}


/*************************************************/
/** 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] == ' ' || !isalpha(w[-1]))
			{
			w[-1] = '\0';
			break;
			}
		}
	*w = '\0';
	return wordbuf;
	}


/**********************************************/
void htmlchar(char c)
	{
	if (c=='<' || c=='>' || c=='&' || c == 0x0d || (c<32 && c!='\t' && c!=0x0a))
		{
		switch(c)
			{
			case '<':
				printf("&lt;");
				break;
			case '&':
				printf("&amp;");
				break;
			case '>':
				printf("&gt;");
				break;
			default:
				break;
			}
		}
	else printf("%c",c);
	return;
	}

/**********************************************/
void filterfile(char *file)
	{
	FILE *fp;
	char c;
	char modstr[]=MODDATEPREFIX;
	int hits=0,n;

	if (fp=fopen(file,"r"))
		{
		while (!feof(fp))
			{
			c = fgetc(fp);
			if (c==modstr[hits])
				{
				while (c == modstr[hits] && !feof(fp) && hits<strlen(modstr))
					{
					hits++;
					c = fgetc(fp);
					}

				if (hits==strlen(modstr))
					{
/* ok.. its the last modified string so delete everything up until the '.' */
/* by delete I mean just don't output it                                   */

					while (c!='.' && !feof(fp))
						c = fgetc(fp);

					hits = 0;
					continue;
					}
				else
					{
					for (n=0;n<hits;n++)
						htmlchar(modstr[n]);
					hits = 0;
					}
				}

			htmlchar(c);
			}
		fclose(fp);
		}
	}

/**********************************************/
void editfile(char *file, char *pass)
	{
	FILE *fp;
	int n;
	char c;

	printf("<H1>Edit WWW Page</H1>\n");

	printf("<FORM ACTION=author METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\"><HR>\n");

	printf("Filename:<BR> <INPUT TYPE=text NAME=openfile VALUE=\"%s\" SIZE=20><BR>\n",file);
	printf("Password:<BR> <INPUT TYPE=password NAME=password VALUE=\"%s\" SIZE=20><BR>\n",pass);

	printf("<HR>");

	printf("<H3>TEXT EDITOR</H3><TEXTAREA ROWS=30 COLS=80 NAME=textbody>");

	filterfile(file);

	printf("</TEXTAREA>");

	printf("<HR><INPUT TYPE=submit VALUE=\"Save\"><INPUT TYPE=reset>\n");
	printf("</FORM><HR>");

	printf("<FORM ACTION=author METHOD=GET ENCTYPE=\"application/x-www-form-urlencoded\">\n");
	printf("<INPUT TYPE=submit VALUE=\"Abort\">\n");
	printf("Quit without saving, returns to Open File page.</FORM><HR>");

	printf("<FORM ACTION=author METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\">\n");
	printf("<INPUT TYPE=hidden VALUE=%s NAME=REVERT>",file);
	printf("<INPUT TYPE=hidden VALUE=%s NAME=REVERTPASS>",pass);
	printf("<INPUT TYPE=submit VALUE=\"Revert to backup\">\n");
	printf("Revert to older version of file.</FORM><HR>");

	printf("<HR><i>HTML authoring system %s</i> (c) 1994 Takoyaki Software Ltd.",VERSION);


	}

/**********************************************/

#if 0

/* not needed with new system */

char *EncodePassword(char *pass)
	{
	static char newpass[20];
	char pwd[20];
	int n;

	if (strlen(pass)>10) pass[10]='\0';

	if (strlen(pass)<6)
		sprintf(pwd,"%s%s",pass,pass);
	else strcpy(pwd,pass);

	if (strlen(pwd)&1) strcpy(pwd+strlen(pwd),"Z");

	for (n=0;n<strlen(pwd);n+=2)
		{
		newpass[n/2] = (((pwd[n]-32)+pwd[n+1]-32)&31)+65;
		}

	newpass[n/2] = '\0';

	return newpass;
	}

#endif

/**********************************************/
void writefile(char *file, char *data)
	{
	FILE *fp;
	time_t tim;
	char buf[100];

	makebackup(file);

	if (fp=fopen(file,"w"))
		{
	
		fwrite(data,strlen(data),1,fp);

		time(&tim);
		strftime(buf,100,"%y/%m/%d at %I:%M%p (JST)",localtime(&tim));
		fprintf(fp,"%s%s</I>.",MODDATEPREFIX,buf);

		fclose(fp);
		}
	}
/**********************************************/
int scanfile(char *fn,char *s)
	{
	int n=0;
	char c;
	FILE *fp;

	if (s[0]=='\0') return 1;

	if (fp = fopen(fn,"r"))
		{

		while (!feof(fp))
			{
			c = fgetc(fp);
			if (s[n] == c)
				{
				n++;
				if (s[n]=='\0')
					{
					c = fgetc(fp);
					fclose(fp);
					if (isalpha(c)) return 0;
					return 1;
					}
				}
			else n=0;
			}
		fclose(fp);
		}
	else return 0;

	return 0;
	}



/**********************************************/
int checkpass(char *fn, char *pass)
	{
	char buf[128];

	if (!pass) return 0;

/* add a little more security here   */
/* only allow one '/' in the file    */
/* this is because this program is   */
/* run with root access so you       */
/* could edit any file on the system */
/* this limits the range             */

		if (stricmp(CONFIGFILE,fn))
			if (strchr(fn,'/'))
				if (strchr(strchr(fn,'/')+1,'/')) return 0;

	if (strlen(pass)>100) pass[100]='\0';

	sprintf(buf,"*admin-override: %s",pass);
	if (scanfile(CONFIGFILE,buf))
		{

		return 1;
		}

	sprintf(buf,"*%s: %s",fn,pass);
	return scanfile(CONFIGFILE,buf);
	}

/**********************************************/
void openfilerequest()
	{
	printf("<H1>Open File To Edit</H1>\n");

	printf("<FORM ACTION=author METHOD=POST ENCTYPE=\"application/x-www-form-urlencoded\"><HR>\n");

	printf("Filename:<BR> <INPUT TYPE=text NAME=openfile SIZE=20><BR>\n");
	printf("Password:<BR> <INPUT TYPE=password NAME=password SIZE=20><BR>\n");

	printf("<HR><INPUT TYPE=submit VALUE=\"Open File\"> <INPUT TYPE=reset>\n");
	printf("<HR></FORM>");

	}


/**********************************************/
/* the starting point of life as we know it   */

main(int argc, char *argv[]) {
	register int x,m=0,n;
	int cl;
	FILE *fp;
	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);

/* go to the src directory */

	chdir(WWWPAGES);

/*********************************************************/
/**  Deal with GET requests                             **/
/**                                                     **/
/*********************************************************/

/* 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)
			{
			max = 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;
			}

		if ((n = findname("OPENFILE")) != -1)
			{

			if (stricmp(entry(n,val),"config")==0) entries[n].val=strdup(CONFIGFILE);

			if (checkpass(entry(n,val),entry(findname("PASSWORD"),val)))
				{
				editfile(entry(n,val),entry(findname("PASSWORD"),val));
				}
			else
				{
				printf("<H1>Open File</H1>");
				printf("The password you specified was incorrect.<HR>\n");
				}
			return;
			}

		openfilerequest();
		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                                     */
/*                                                                    */
/**********************************************************************/


	if ((n = findname("REVERT"))!=-1)
		{
		revert(entry(n,val),entry(findname("REVERTPASS"),val));
		}

	if ((n = findname("textbody"))!=-1)
		{
		printf("<H1>Save File</H1>");
		if (checkpass(entry(findname("OPENFILE"),val),entry(findname("PASSWORD"),val)))
			{
			writefile(entry(findname("OPENFILE"),val),entry(n,val));
			printf("<HR>File successfully saved<HR>");
			}
		else
			{
			printf("The password you specified was incorrect.<HR>\n");
			}
		return;
		}

	if ((n = findname("OPENFILE"))!=-1)
		{

		if (stricmp(entry(n,val),"config")==0) entries[n].val=strdup(CONFIGFILE);

		if (checkpass(entry(n,val),entry(findname("PASSWORD"),val)))
			{
			editfile(entry(n,val),entry(findname("PASSWORD"),val));
			}
		else
			{
			printf("<H1>Open File</H1>");
			printf("The password you specified was incorrect.<HR>\n");
			}

		return;
		}

}

/* LE FIN */

