/* HTTP server
 * A nearly complete rewrite, extensively expanded, based loosely on
 * the HTTP server of Selcuk Ozturk <seost2+@pitt.edu> &
 * <ashok@biochemistry.cwru.edu>, which were based on
 * Chris McNeil's <cmcneil@mta.ca> Gopher server, which was based in
 * part on fingerd.c
 *
 */

#include "global.h"
#include "ctype.h"
#include "commands.h"
#ifdef MSDOS
#include "hardware.h"
#else
#include <time.h>
#include "mbuf.h"
#include "socket.h"
#include "session.h"
#endif
#if defined(HTTP) || defined(BROWSER)
#include <sys/stat.h>
#include "files.h"
#include "mailbox.h"
#include "smtp.h"
#include "browser.h"
#include "usock.h"
#ifdef HTTPPBBS
#include "domain.h"
#include "bm.h"
#endif
#endif
#ifdef STAT_HTTP
#include "stats.h"
#endif

#ifdef HTTP


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: http.c,v 1.36 2001/05/06 16:32:56 brian Exp $";
#endif

#ifdef NO_SETENV
char *setenv (char const *, char const *, int);
#endif

#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
#define HTTP_TIME_FORMAT "%a, %d %b %Y %T GMT\n"
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%y %T %Z"
#define SIZEFMT_BYTES 0
#define SIZEFMT_KMG 1
#define SECS_IN_6MONTHS 15768000 /* seconds in 1/2 year */

static char entry[] = "<IMG SRC=\"/icons/%s.xbm\" ALT=\"%s\"> ";
static char entry1[] = "<A HREF=\"/%s%s%s\">%-s</a>";
static char entry2[] = "%2d-%s-%02d %02d:%02d %6dK\n";
int HttpUsers = 0;
static int Httpmax = 10;
static int Httpsimc = 5;
static int HttpSessions = 0;
static int Httptdisc = 180;
static int HTTPreferer = 1;
static int HTTPagent = 1;
static int HTTPcookie = 0;
static int HTTPusecookies = 0;
static int HTTPmisc = 1;
static int HTTPpost = 1;
static int HTTPexeccmd = 0;
static int HTTPstatusURL = 1;
#ifdef HTTPCGI
static int HTTPexeccgi = 0;
#endif
#ifdef STRICT_HTTPCALL
static int HTTPstrict = 0;
#endif
#ifdef HTTPPBBS
static int HTTPexecbbs = 0;
static int HTTPanonsend = 0;
extern int32 MbAmprPerms;
extern int32 MbNonAmprPerms;
extern int32 MbHttpPerms;
extern uint32 NonSecureAmpr;
#endif
extern time_t StartTime;
#if 0
static int HTTPscript = 0;
#endif
static FILE * HTTPsavedfp;
static char *HTTPHostname;
static char *HTTPFtpdir = NULLCHAR;

#ifdef MSDOS
#define HTMLEXT	"htm"
#else
#define HTMLEXT "html"
#endif

struct reqInfo {
	int		port;
	int		index;
#if 0
	int		response;
#endif
	int		qsize;		/* not used at the present */
	int		sizefmt;
	int		isvolatile;
#ifdef HTTPPBBS
	int		msgcount;
	int		newmsgcount;
	int		msgnum;
	int		msgcurrent;
	int		anony;
	int		sock;
	long		privs;
#endif
	char		*hostname;
	char		*url;
	char		*method;
	char		*version;
	char		*newcheck;
	char		*from;
	char		*referer;
	char		*agent;
	char		*passwd;
	char		*dirname;
	char		*ftpdirname;
	char 		*usetime;
	char		*useerror;
	char		*realm;
	char		*cookie;
	char		*rmtaddr;
	char		*rmthost;
	char		buf[PLINELEN + 16];
#ifdef HTTPPBBS
	char		*areaname;
	char		*username;
	struct mbx 	*m;
	char		*bbspath;
#endif
};


struct secureURL	{
	char		 *url;
	char		 *pwfile;
	char		 *realm;
	struct secureURL *next;
};
#define NULLSECUREURL ((struct secureURL *)0)

static struct secureURL *securedURLS = NULLSECUREURL;


/* function prototypes */
#ifdef HTTPCGI
static void setup_cgi_variables (struct reqInfo *rq, char *name);
#endif
static int dohttpagent (int argc,char *argv[],void *p);
static int dohttpexeccmd (int argc,char *argv[],void *p);
static int dohttpstatusurl (int argc,char *argv[],void *p);
static int dohttphostname (int argc,char *argv[],void *p);
#ifdef HTTPCGI
static int dohttpexeccgi (int argc,char *argv[],void *p);
#endif
#ifdef HTTPPBBS
static int dohttpexecbbs (int argc,char *argv[],void *p);
static int checkareaperms (struct reqInfo *rq);
#endif
static int dohttpmax (int argc,char *argv[],void *p);
static int dohttpmisc (int argc,char *argv[],void *p);
static int dohttppost (int argc,char *argv[],void *p);
static int dohttpreferer (int argc,char *argv[],void *p);
static int dohttpcookielog (int argc,char *argv[],void *p);
static int dohttpcookies (int argc,char *argv[],void *p);
static int dohttpsecure (int argc,char *argv[],void *p);
#if 0
static int dohttpscript (int argc,char *argv[],void *p);
#endif
static int dohttpsim (int argc,char *argv[],void *p);
static int dohttpstatus (int argc,char *argv[],void *p);
static int dohttptdisc (int argc,char *argv[],void *p);
static int dohttpftpdir (int argc,char *argv[],void *p);

#ifdef STRICT_HTTPCALL
static int dohttpstrict (int argc,char *argv[],void *p);
#endif

static char *assigncookie (struct reqInfo *rq, time_t thetime);
static void httpserv (int s,void *unused,void *p);
static int mkwelcome (const char *Hdir,char *file, int ftpfile, const char *Ftpdir);
static char * decode (char *string);
static void httpHeaders (int type, const char *str, struct reqInfo *rq, int more, const char *realm);
static void httpFileinfo (FILE *fp, struct reqInfo *rq, int thetype, char *vers);
static void openHTTPlog (const char *name);
static void closeHTTPlog (void);
static int getmonth (char *cp);
static int isnewer (time_t thetime,char *tmstr);
static long countusage (const char *file, const char *basedir, int display, int increase);
static void sendhtml (FILE *fp, int firstfile, struct reqInfo *rq);
static void file_or_virtual (char *cp, char *newname, struct reqInfo *rq);
static void send_size (int sizefmt,long size);
static char *ht_time (time_t t,const char *fmt,int gmt);
static char *gmt_ptime (time_t *thetime);
static int addFile (const char *fname, int s);
static struct secureURL *secured_url (struct reqInfo *rq);
static int is_authorized (struct reqInfo *rq);
static int http_authcheck (char *filename, char *username, char *pass);

#ifdef HTTPPBBS
static void addUserFile (struct reqInfo *rq);
static void addMsgFile (char *file, struct reqInfo *rq);
static void doPBBScmd (struct reqInfo *rq, const char *cmdstring);
static void doPBBScmd_init (struct reqInfo *rq);
static void doPBBScmd_term (struct reqInfo *rq);
static void doPBBScmd_exit (struct reqInfo *rq);
static int dohttpanonsend (int argc,char *argv[],void *p);
#endif

extern char *base64ToStr (char *b64);
#ifdef ALLSERV
extern char *getquote (void);
#endif

/* HTTP Header lines sent or processed */
#define HDR_TYPE		0
#define HDR_LENGTH		1
#define HDR_MIME		2
#define HDR_SERVER		3
#define HDR_LOCATION		4
#define HDR_DATE		5
#define HDR_MODIFIED		6
#define HDR_SINCE		7
#define HDR_REFERER		8
#define HDR_AGENT		9
#define HDR_FROM		10
#define HDR_EXPIRES		11
#define HDR_AUTHORIZE		12
#define HDR_AUTHENTICATE	13
#define HDR_COOKIE		14

static const char *HTTPHdrs[] = {
	"Content-Type:",
	"Content-Length:",
	"MIME-version:",
	"Server:",
	"Location:",
	"Date:",
	"Last-Modified:",
	"If-Modified-Since:",
	"Referer:",
	"User-Agent:",
	"From:",
	"Expires:",
	"Authorization:",
	"WWW-Authenticate:",
	"Cookie:"
};


/* First component of MIME types */

static const char *CTypes1[] = {
	"video/",
	"text/",
	"image/",
	"audio/",
	"application/"
};


/* Struct for looking up MIME types from file extensions */
struct FileTypes {
	const char *ext;
	short type1;
	const char *type2;
};


static struct FileTypes HTTPtypes[] = {
	/* Other 'special' entries can be added in front of 'html',
	 * but care must be taken to make sure that the defines for
	 * HTTPbinary, HTTPplain, HTTPform, HTTPhtml and HTTPhtm are correct
	 * and that no entries with a 'NULLCHAR' ext come after 'html'
	 * except for the one that ends the table.
	 */
#define HTTPbinary 0
	{ NULLCHAR,	4,	"octet-stream" },
#define HTTPplain 1
	{ NULLCHAR,	1,	"plain" },
#ifndef _lint
#define HTTPform 2
#endif
	{ NULLCHAR,	4,	"x-www-form-urlencoded" },
#define HTTPhtml 3
	{ "html",	1,	"html" },
#define HTTPhtm 4
	{ "htm",	1,	"html" },
	/* the following can be in any order */
	{ "avi",	0,	"x-msvideo" },
	{ "qt",		0,	"quicktime" },
	{ "mov",	0,	"quicktime" },
	{ "mpeg",	0,	"mpeg" },
	{ "mpg",	0,	"mpeg" },
	{ "rtx",	1,	"richtext" },
	{ "xpm",	2,	"x-xpixmap" },
	{ "xbm",	2,	"x-xbitmap" },
	{ "rgb",	2,	"x-rgb" },
	{ "ppm",	2,	"x-portable-pixmap" },
	{ "pgm",	2,	"x-portable-graymap" },
	{ "pbm",	2,	"x-portable-bitmap" },
	{ "pnm",	2,	"x-portable-anymap" },
	{ "ras",	2,	"x-cmu-raster" },
	{ "tiff",	2,	"tiff" },
	{ "tif",	2,	"tiff" },
	{ "jpeg",	2,	"jpeg" },
	{ "jpg",	2,	"jpeg" },
	{ "gif",	2,	"gif" },
	{ "wav",	3,	"x-wav" },
	{ "aiff",	3,	"x-aiff" },
	{ "aif",	3,	"x-aiff" },
	{ "au",		3,	"basic" },
	{ "snd",	3,	"basic" },
	{ "tar",	4,	"x-tar" },
	{ "man",	4,	"x-trof-man" },
	{ "rtf",	4,	"rtf" },
	{ "eps",	4,	"postscript" },
	{ "ps",		4,	"postscript" },
	{ "sit",	4,	"x-stuffit" },
	{ "hqx",	4,	"mac-binhex40" },
	{ "fif",	4,	"fractals" },
	{ "zip",	4,	"x-zip" },
	{ "gz",		4,	"x-gzip" },
	{ "z",		4,	"x-compress" },
	{ NULLCHAR,	-1,	NULLCHAR }
};


/* Struct to keep tract of ports defined and their root directory */

#define MAXHTTPPORTS 10
struct portsused {
	int	port;
	int	sock;
	uint32	address;
	char	*dirname;
	char	*ftpdirname;
	char	*hostname;
	long	requests;
	long	homehits;
	long	stathits;
#ifdef HTTPPBBS
	long	bbshits;
#endif
};
static struct portsused ports[MAXHTTPPORTS];


#ifdef UNIX
#define OS "Unix"
#define WILDCARD "*"
#else
#define OS "MS-DOS"
#define WILDCARD "*.*"
#endif



static struct cmds Httpcmds[] = {
	{ "agentlogging",	dohttpagent,	0, 0, NULLCHAR },
#ifdef HTTPPBBS
	{ "anonsend",		dohttpanonsend,	0, 0, NULLCHAR },
#endif
	{ "cookielogging",	dohttpcookielog,0, 0, NULLCHAR },
#ifdef HTTPPBBS
	{ "execbbs",		dohttpexecbbs,	0, 0, NULLCHAR },
#endif
#ifdef HTTPCGI
	{ "execcgi",		dohttpexeccgi,	0, 0, NULLCHAR },
#endif
	{ "execcmd",		dohttpexeccmd,	0, 0, NULLCHAR },
	{ "ftpdir",		dohttpftpdir,	0, 0, NULLCHAR },
	{ "hostname",		dohttphostname, 0, 0, NULLCHAR },
	{ "maxcli",		dohttpmax,	0, 0, NULLCHAR },
	{ "misclogging",	dohttpmisc,	0, 0, NULLCHAR },
	{ "postlogging",	dohttppost,	0, 0, NULLCHAR },
	{ "refererlogging",	dohttpreferer,	0, 0, NULLCHAR },
#if 0
	{ "script",		dohttpscript,	0, 0, NULLCHAR },
#endif
	{ "secure",		dohttpsecure,	0, 0, NULLCHAR },
	{ "simult",		dohttpsim,	0, 0, NULLCHAR },
	{ "status",		dohttpstatus,	0, 0, NULLCHAR },
	{ "statusurl",		dohttpstatusurl,0, 0, NULLCHAR },
#ifdef STRICT_HTTPCALL
	{ "strictcall",		dohttpstrict,	0, 0, NULLCHAR },
#endif
	{ "tdisc",		dohttptdisc,	0, 0, NULLCHAR },
	{ "usecookies",		dohttpcookies,	0, 0, NULLCHAR },
	{ NULLCHAR,		NULL,		0, 0, NULLCHAR }
};



int
dohttp (int argc, char *argv[], void *p)
{
	return subcmd (Httpcmds, argc, argv, p);
}



static int
dohttpstatus (int argc OPTIONAL, char *argv[], void *p)
{
int found = 0, k;

	for (k = 0; k < MAXHTTPPORTS; k++)	{
		if (ports[k].port)	{
			found++;
			tprintf ("HTTP Server active on %s port #%-4d - %s\n", inet_ntoa (ports[k].address), ports[k].port, ports[k].dirname);
		}
	}
	tputs ("    ");
	if (!found)
		tputs ("No");
	else
		tprintf ("%d", found);
	tprintf (" HTTP Server%s %s active\n\n", (found == 1) ? "" : "s", (found == 1) ? "is" : "are");

	(void) dohttpagent (0, argv, p);
#ifdef HTTPPBBS
	(void) dohttpanonsend (0, argv, p);
#endif
	(void) dohttpcookielog (0, argv, p);
#ifdef HTTPPBBS
	(void) dohttpexecbbs (0, argv, p);
#endif
#ifdef HTTPCGI
	(void) dohttpexeccgi (0, argv, p);
#endif
	(void) dohttpexeccmd (0, argv, p);
	(void) dohttpftpdir (0, argv, p);
	(void) dohttphostname (0, argv, p);
	(void) dohttpmax (0, argv, p);
	(void) dohttpmisc (0, argv, p);
	(void) dohttppost (0, argv, p);
	(void) dohttpreferer (0, argv, p);
	(void) dohttpsecure (0, argv, p);
	(void) dohttpsim (0, argv, p);
#ifdef STRICT_HTTPCALL
	(void) dohttpstrict (0, argv, p);
#endif
	(void) dohttptdisc (0, argv, p);
	(void) dohttpcookies (0, argv, p);
	tputc ('\n');
	return 0;
}



static int
dohttpexeccmd (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPexeccmd, "Enable Server-side include 'exec cmd's", argc, argv);
}



static int
dohttpstatusurl (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPstatusURL, "Enable status URL (/status)", argc, argv);
}



#ifdef HTTPCGI
static int
dohttpexeccgi (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPexeccgi, "Enable Server-side include 'exec cgi's", argc, argv);
}
#endif



#ifdef STRICT_HTTPCALL
static int
dohttpstrict (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPstrict, "Allow HTTP access from callsigns, only: ", argc, argv);
}
#endif



#ifdef HTTPPBBS
static int
dohttpexecbbs (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPexecbbs, "Enable Server-side include 'exec bbs's", argc, argv);
}



static int
dohttpanonsend (int argc, char *argv[], void *p OPTIONAL)
{
	/* related to the NO_HTTP_MAIL permission */
	return setbool (&HTTPanonsend, "Allow anonymous users to SEND mail", argc, argv);
}
#endif



#if 0
static int
dohttpscript (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPscript, "Enable Server-side include 'tscript's", argc, argv);
}
#endif



static int
dohttpsecure (int argc, char *argv[], void *p OPTIONAL)
{
struct secureURL *sec;
int didheader = 0;

	if (argc == 1)	{
		/* display currently defined secured URL's */
		for (sec = securedURLS; sec != NULLSECUREURL; sec = sec->next)	{
			if (!didheader)	{
				didheader = 1;
				tprintf ("\nSecured URLs:\n");
			}
			tprintf ("/%s (%s): %s\n", sec->url, sec->realm, sec->pwfile);
		}
		if (didheader)
			tputc ('\n');
		return 0;
	}

	if (argc != 4)	{
		tputs ("usage: http secure <url_or_dir> <realmstring> <pwdfile>\n");
		return 1;
	}

	if (argv[1][0] != '/')	{
		tputs ("The url MUST begin with a '/'\n");
		return 1;
	}

	if (argv[3][0] != '/')	{
		tputs ("The pwdfile MUST be a complete pathname, starting with a '/'\n");
		return 1;
	}

	sec = (struct secureURL *) callocw (1, sizeof (struct secureURL));
	sec->next = securedURLS;
	securedURLS = sec;
	sec->realm = strdup (argv[2]);
	sec->pwfile = strdup (argv[3]);
	sec->url = strdup (&argv[1][1]);
	return 0;
}



static int
dohttpreferer (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPreferer, "Log Referer Headers", argc, argv);
}



static int
dohttpcookielog (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPcookie, "Log Incoming Cookie Headers", argc, argv);
}



static int
dohttpcookies (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPusecookies, "Assign Cookies", argc, argv);
}



static int
dohttpagent (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPagent, "Log User-Agent Headers", argc, argv);
}



static int
dohttpmisc (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPmisc, "Log Misc Headers", argc, argv);
}



static int
dohttppost (int argc, char *argv[], void *p OPTIONAL)
{
	return setbool (&HTTPpost, "Log POST Requests", argc, argv);
}



static int
dohttpmax (int argc, char *argv[], void *p OPTIONAL)
{
	return setint (&Httpmax, "Max. HTTP connects", argc, argv);
}



static int
dohttpsim (int argc, char *argv[], void *p OPTIONAL)
{
	return setint (&Httpsimc, "Simult. HTTP conn.'s serviced", argc, argv);
}



static int
dohttptdisc (int argc, char *argv[], void *p OPTIONAL)
{
	return setint (&Httptdisc, "HTTP Server tdisc (sec)", argc, argv);
}



static int
dohttphostname (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)	{
		if (HTTPHostname == NULLCHAR && Hostname)
			HTTPHostname = strdup (Hostname);
		if (HTTPHostname)
			tprintf ("Default HTTP Hostname: %s\n", HTTPHostname);
		return 0;
	}
	if (HTTPHostname)
		free (HTTPHostname);
	HTTPHostname = strdup (argv[1]);
	return 0;
}



static int
dohttpftpdir (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)	{
		tprintf ("Default HTTP FTP directory: %s\n", (HTTPFtpdir) ? HTTPFtpdir : "none - disabled");
		return 0;
	}
	if (HTTPFtpdir)
		free (HTTPFtpdir);
	HTTPFtpdir = strdup (argv[1]);
	return 0;
}



/* Start up http service */
int
httpstart (int argc, char *argv[], void *p OPTIONAL)
{
int port, k;
uint32 address = INADDR_ANY;

	if (HTTPHostname == NULLCHAR) 	{
		if (Hostname)
			HTTPHostname = strdup (Hostname);
		else
			HTTPHostname = strdup ("localhost");
	}

	if(argc < 2 || argv[1][0] == '-')
		port = IPPORT_HTTP;
	else	{
		if (argv[1][0] == '?')	{
			/* they don't really want to start it, they want usage info */
			tprintf ("usage: start http [<port> [<rootdir> [<hostname> [<ipaddress>] [ftpdir]]]]\n");
			return 0;
		}
		port = atoi(argv[1]);
	}

	if (argc > 4 && argv[4][0] != '-')	{
		address = resolve (argv[4]);
		if (address == 0L)	{
			tprintf ("IP address %s does NOT resolve! HTTP server NOT started on port %d!\n",
				argv[4], port);
			return 0;
		}
	}

	for (k = 0; k < MAXHTTPPORTS; k++) {
		if (ports[k].port == port && ports[k].address == address)	{
			tprintf ("Sorry, but you already have an HTTP server on port #%d for that address!\n", port);
			return 0;
		}
	}

	for (k = 0; k < MAXHTTPPORTS; k++) {
		if (ports[k].sock <= 0)
			break;
	}
	if (k == MAXHTTPPORTS)	{
		tprintf ("Sorry, but all %d HTTP ports are assigned!\n", MAXHTTPPORTS);
		return 0;
	}

	if(argc < 3 || argv[2][0] == '-')
		ports[k].dirname = strdup (HTTPdir);
	else
		ports[k].dirname = strdup (argv[2]);

	if (argc < 4 || argv[3][0] == '-')
		ports[k].hostname = strdup (HTTPHostname);
	else
		ports[k].hostname = strdup (argv[3]);

	if (argc < 5 || argv[4][0] == '-')
		ports[k].ftpdirname = (HTTPFtpdir) ? strdup (HTTPFtpdir) : NULLCHAR;
	else
		ports[k].ftpdirname = strdup (argv[4]);

	ports[k].sock = -1;
	ports[k].port = port;
	ports[k].address = address;

	return (installserver (argc, argv, &ports[k].sock, "HTTP Listener", ports[k].port,
		ports[k].address, "HTTP Server", httpserv, 1024, (void *)k));
}



int
http0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
int port, k;

	if(argc < 2)
		port = IPPORT_HTTP;
	else
		port = atoi(argv[1]);
	for (k = 0; k < MAXHTTPPORTS; k++) {
		if (ports[k].port == port)	{
			ports[k].port = 0;
			free (ports[k].dirname);
			ports[k].dirname = NULLCHAR;
			free (ports[k].hostname);
			ports[k].hostname = NULLCHAR;
			return (deleteserver (&ports[k].sock));
		}
	}
	tprintf ("Sorry, but no HTTP server was found on port #%d!\n", port);
	return 0;
}



/* Creates a copy of string which has '%' escaped charecters decoded
   back to ASCII. Allocates memory. Caller must free it. */


static char *
decode (char *string)
{
int i,k,slen;
char code[3],*tmp;

	slen = (int) strlen (string);
	tmp = mallocw ((unsigned int) (slen + 1));
	i = k = 0;

	while (i <= slen) {
		if (string[i] == '%') {
			string[i] = '#';        /* '%' sign causes problems in log()  */
			code[0] = string[i+1];
			code[1] = string[i+2];
			code[2] = '\0';
			tmp[k] = (char)htoi(code);
			k++;  i += 3;
		} else
			tmp[k++] = string[i++];
	}
	return tmp;
}



#ifdef HTTPPBBS
static void
doPBBScmd (struct reqInfo *rq, const char *cmdstr)
{
	doPBBScmd_init (rq);
	strncpy (rq->m->line, cmdstr, MBXLINE);
	doPBBScmd_term (rq);
}



static void
doPBBScmd_init (struct reqInfo *rq)
{
	rq->m = newmbx (1);
	rq->m->privs = rq->privs;
	rq->m->sid = MBX_HTTP;
	rq->m->user = Curproc->output;
	rq->m->path = rq->bbspath;
	if (!rq->username)
		rq->username = strdup ("anonymous");
	strncpy (rq->m->name, rq->username, 19);
	if (rq->areaname)
		changearea (rq->m, rq->areaname, 0);
}



static void
doPBBScmd_term (struct reqInfo *rq)
{
	if (rq->m)	{
		(void) mbx_parse (rq->m);
		doPBBScmd_exit (rq);
	}
}



static void
doPBBScmd_exit (struct reqInfo *rq)
{
	if (rq->m)	{
		rq->m->path = NULLCHAR;
		setlastread (rq->m);
		exitbbs(rq->m);
		rq->m = NULLMBX;
	}
}



static void
addUserFile (struct reqInfo *rq)
{
char fname[256];

	sprintf (fname, "%s/bbs/user.%s", rq->dirname, HTMLEXT);
	addMsgFile (fname, rq);
}



static void
addMsgFile (char *fname, struct reqInfo *rq)
{
FILE *fp;

	if ((fp = fopen (fname, READ_BINARY)) != NULLFILE) {
		sendhtml (fp, 1, rq);
		(void) fclose (fp);
	}
}



static int
checkareaperms (struct reqInfo *rq)
{
int retval = 0;

	/* only return a 1 if (1) it's our area, (2) we are a sysop, or
	   (3) it is a public area. Else, return a zero
	 */

	if (!stricmp(rq->areaname, rq->username) || (rq->privs & SYSOP_CMD) || isarea(rq->areaname))
		retval = 1;
	return retval;
}

#endif



static int
addFile (const char *fname, int s)
{
FILE *fp;

	if ((fp = fopen (fname, READ_BINARY)) != NULLFILE) {
		(void) sendfile (fp, s, IMAGE_TYPE, 0);
		(void) fclose (fp);
		return 1;
	}
	return 0;
}



static void
httpserv (int s, void *v1, void *p OPTIONAL)
{
char command[PLINELEN], *cp, *newcommand, *tmp, *version;
FILE *fp = NULLFILE;
int port, length, i, err = 1, head = 0, retval = 200;
struct stat sb;
int isdir = 0;
struct reqInfo *rq;
char poststr[PLINELEN + 16];
int posted_message = 0;
struct sockaddr fsocket;
uint32 addr;
register struct usock *up;
int isValidFtpRequest = 0;
time_t nowtime;

	(void) sockowner (s, Curproc);
	(void) sockmode (s, SOCK_ASCII);

	chname (Curproc, "HTTP Server - Init");
	rq = (struct reqInfo *)callocw (1, sizeof (struct reqInfo));
	if (rq == (struct reqInfo *)0)
		return;
		
	rq->sizefmt = SIZEFMT_KMG;
	rq->useerror = strdup (DEFAULT_ERROR_MSG);
	rq->usetime = strdup (DEFAULT_TIME_FORMAT);
	rq->index = (int) v1;
	rq->port = ports[rq->index].port;
	rq->hostname = ports[rq->index].hostname;
	ports[rq->index].requests++;
#ifdef STATS_HTTP
	STATS_addhttp (0);
#endif

	if (getpeername (s, (char *) &fsocket, &i) != -1)	{
		port = DTranslate;
		DTranslate = 0;
		rq->rmtaddr = strdup (psocket (&fsocket));
		if ((cp = strchr (rq->rmtaddr, ':')) != NULLCHAR)
			*cp = 0;
		addr = aton (rq->rmtaddr);
		DTranslate = 1;
		rq->rmthost = strdup (inet_ntoa (addr));
		DTranslate = port;
	} else	{
		rq->rmthost = strdup ("unknown");
		rq->rmtaddr = strdup ("unknown");
	}

	close_s(Curproc->output);
	close_s(Curproc->input);
	Curproc->output = Curproc->input = s;
	rq->sock = s;

	/* see if we have too many connections */
	if (Httpmax <= HttpUsers)	{
		tprintf("HTTP/1.0 503 Service Unavailable\n%s %s%s\n\n<TITLE>Busy</TITLE><H1>Busy</H1>\nTry again later.\n",HTTPHdrs[0],CTypes1[HTTPtypes[HTTPhtml].type1],HTTPtypes[HTTPhtml].type2);
		HttpUsers++;
		goto quit;
	}

	HttpUsers++;
#ifdef STATS_USE
	STATS_adduse (1);
#endif
	sprintf (command, "HTTP Server - Waiting (%s)", rq->rmthost);
	chname (Curproc, command);

	/* only allow Httpsimc connections to be active at a time */
	while (HttpSessions >= Httpsimc)	{
		kalarm (5000L);
		kwait(&HttpSessions);
		kalarm (0L);
		if((up = itop(s)) == NULLUSOCK || up->cb.p == NULLCHAR)
			break;
	}
	HttpSessions++;

	kalarm (Httptdisc * 1000L);
	if (recvline (s, (unsigned char *) command, PLINELEN) == -1)
		goto quit;
	kalarm (0L);

	sprintf (poststr, "HTTP Server - Processing (%s)", rq->rmthost);
	chname (Curproc, poststr);

	port = ports[(int)v1].port;
	rq->dirname = ports[rq->index].dirname;
	if (ports[rq->index].ftpdirname == NULLCHAR && HTTPFtpdir)
		/* newly defined default - allow it to set after the fact */
		ports[rq->index].ftpdirname = strdup (HTTPFtpdir);
	rq->ftpdirname = ports[rq->index].ftpdirname;
	
	rip (command);
	if (!*command)	{
		retval = 400;
		usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent a query that this server could not understand.<P>\nReason: Invalid HTTP/0.9 method.<P></BODY>\n");
		goto quit;
	}

	tmp = decode (command);
	cp = strchr (tmp, ' ');
	if (cp == NULLCHAR)	{
		retval = 400;
		usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent a query that this server could not understand.<P>\nReason: No URL given.<P></BODY>\n");
		goto quit;
	}
	*cp++ = 0;
	newcommand = ++cp;
	version = strchr (newcommand, ' ');
	if (version != NULLCHAR)
		*version++ = 0;
	
	/* at this point, tmp=command, newcommand=ulr, and version=httpversion info */
	if (!strcmp (newcommand, "/"))
		newcommand++;
	if (!*newcommand)
		ports[rq->index].homehits++;
	
	rq->url = strdup (newcommand);
	rq->version = strdup (version);
	rq->method = strdup (tmp);
	length = (int) strlen (newcommand);

	/* if HTTP 0.9 (no version string), only 'GET' is allowed */
	if (version == NULLCHAR && strncmp (command, "GET", 3))	{
		retval = 400;
	  	httpHeaders (HTTPhtml, "400 Bad Request", rq, 0, NULLCHAR);
		usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent a query that this server could not understand.<P>\nReason: Invalid HTTP/0.9 method.<P></BODY>\n");
		goto quit0;
	}

	/* not complete yet, but it at least logs it and responds ;-) */
	poststr[0] = 0;
	if (!strncmp (command, "POST", 4)){
		int postsize = 0;
		int headersdone = 0;

		/* if posting PBBS email, open a file for the post contents */
		if (!strcmp (newcommand, "bbs/sendmail"))
			posted_message = 1;

		if (HTTPpost != 0)	{
			openHTTPlog("post");
			simple_log (s, command);
			closeHTTPlog();
		}
		while (kalarm (Httptdisc * 1000L),recvline (s, (unsigned char *) rq->buf, sizeof(rq->buf)) != -1) {
			kalarm (0L);
			if (headersdone)
				postsize -= (int) strlen(rq->buf);
			rip (rq->buf);
			if (HTTPpost != 0)	{
				openHTTPlog("post");
				simple_log (s, rq->buf);
				closeHTTPlog();
			}

			/* if this is an incoming email posting, check for auth string */
			if (posted_message)	{
				/* look for cookie header line */
				if (!strnicmp (rq->buf, HTTPHdrs[HDR_COOKIE], strlen(HTTPHdrs[HDR_COOKIE])))
					rq->cookie = strdup (&rq->buf[strlen(HTTPHdrs[HDR_COOKIE]) + 1]);
				/* also look for auth header line */
				if (!strnicmp (rq->buf, HTTPHdrs[HDR_AUTHORIZE], strlen(HTTPHdrs[HDR_AUTHORIZE])))
					rq->passwd = strdup (&rq->buf[strlen(HTTPHdrs[HDR_AUTHORIZE]) + 7]);
			}

			if (!strnicmp (rq->buf, HTTPHdrs[1], strlen(HTTPHdrs[1])))	{
				postsize = atoi(&rq->buf[strlen(HTTPHdrs[1])]);
				continue;
			} else if (!*rq->buf && !headersdone)	{
				headersdone = 1;
				if (!postsize)	{
					retval = 400;
					httpHeaders (HTTPhtml, "400 Bad Request", rq, 0, NULLCHAR);
					usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent an improperly formatted POST - no size given.</BODY>\n");
					goto quit0;
				}
				/* one more line with content */
				continue;
			}

			if (!headersdone)
				continue;
				
			if (!posted_message)	{
#if 1
				if (postsize <= 0)
					break;
#endif
				continue;
			}

			/* if this is a posted message, this will be our data line - save it */
			strncpy (poststr, rq->buf, PLINELEN + 16);
			break;
		}
		kalarm (0L);

		if (posted_message)
			goto http_bbs_entry;

		/* if this is NOT an email posting, return an okay and bitbucket the posting action */
post_response:
		retval = 202;
		httpHeaders (HTTPhtml, "202 Accepted", rq, 0, NULLCHAR);
		usputs (s, "<HEAD><TITLE>202 Post Accepted</TITLE></HEAD>\n<BODY><H1>202 Post Accepted</H1>\nThe TNOS server has accepted and logged your posting.</BODY>\n");
		goto quit0;
	}

	if (!strncmp (command, "HEAD", 4))
		head = 1;

	if (version != NULLCHAR && !strnicmp (version, "HTTP/", 5)) {
		/* we must wait to receive header lines, until a blank line */
		while (kalarm (Httptdisc * 1000L), recvline (s, (unsigned char *) rq->buf, sizeof(rq->buf)) != -1) {
			kalarm (0L);
			rip (rq->buf);
			if (!*rq->buf)
				break;
			if (!strnicmp (rq->buf, HTTPHdrs[HDR_COOKIE], strlen(HTTPHdrs[HDR_COOKIE])))	{
				rq->cookie = strdup (&rq->buf[strlen(HTTPHdrs[HDR_COOKIE]) + 1]);
				if (HTTPcookie == 0)
					continue;
				openHTTPlog("cookies");
				sprintf (rq->buf, "%s; URL=%s", rq->cookie, rq->url);
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_LENGTH], strlen(HTTPHdrs[HDR_LENGTH]))) {
				rq->qsize = atoi (&rq->buf[strlen(HTTPHdrs[HDR_LENGTH]) + 1]);
				if (HTTPmisc == 0)
					continue;
				openHTTPlog("mischttp");
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_SINCE], strlen(HTTPHdrs[HDR_SINCE]))) {
				rq->newcheck = strdup (&rq->buf[strlen(HTTPHdrs[HDR_SINCE]) + 1]);
				if (HTTPmisc == 0)
					continue;
				openHTTPlog("mischttp");
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_FROM], strlen(HTTPHdrs[HDR_FROM]))) {
				rq->from = strdup (&rq->buf[strlen(HTTPHdrs[HDR_FROM]) + 1]);
				if (HTTPmisc == 0)
					continue;
				openHTTPlog("mischttp");
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_AUTHORIZE], strlen(HTTPHdrs[HDR_AUTHORIZE]))) {
				rq->passwd = strdup (&rq->buf[strlen(HTTPHdrs[HDR_AUTHORIZE]) + 7]);
				if (HTTPmisc == 0)
					continue;
				openHTTPlog("mischttp");
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_REFERER], strlen(HTTPHdrs[HDR_REFERER]))) {
				rq->referer = strdup(&rq->buf[strlen(HTTPHdrs[HDR_REFERER]) + 1]);
				if (HTTPreferer == 0)
					continue;
				openHTTPlog("referer");
				sprintf (rq->buf, "%s -> /%s", rq->referer, rq->url);
			} else if (!strnicmp (rq->buf, HTTPHdrs[HDR_AGENT], strlen(HTTPHdrs[HDR_AGENT])))	{
				rq->agent = strdup(&rq->buf[strlen(HTTPHdrs[HDR_AGENT]) + 1]);
				if (HTTPagent == 0)
					continue;
				openHTTPlog("agent");
				sprintf (rq->buf, "%s -> /%s", rq->agent, rq->url);
			} else	{
				if (HTTPmisc == 0)
					continue;
				openHTTPlog("mischttp");
			}
			simple_log (s, rq->buf);
			closeHTTPlog();
		}
		kalarm (0L);
	}
	
	if (!head && strncmp (command, "GET", 3))	{
		/* not GET, HEAD, or POST, so we don't support it.... */
		retval = 400;
	  	httpHeaders (HTTPhtml, "400 Bad Request", rq, 0, NULLCHAR);
		usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent a query that this server could not understand.<P>\nReason: Invalid or unsupported method.<P></BODY>\n");
		goto quit0;
	}

	if (HTTPstatusURL && (!strcmp (newcommand, "status") || !strcmp (newcommand, "status/")))	{
		ports[rq->index].stathits++;
		rq->isvolatile = 1;
	  	httpHeaders (HTTPhtml, "200 Document follows", rq, 0, NULLCHAR);
		usprintf (s, "<HEAD><TITLE>Server Status at %s</TITLE></HEAD>\n", ports[rq->index].hostname);
		usputs (s, "<HTML><BODY><A HREF=\"http://www.lantz.com/tnos/\"><IMG SRC=\"http://www.lantz.com/tnos/tnos.gif\" align=left></A>");
		usprintf (s, "<H1>%s:%d</H1>\n<H2>Server Info</H2>\n", ports[rq->index].hostname, ports[rq->index].port);

		cp = ctime (&StartTime);
		rip (cp);
		usprintf (s, "<TABLE border=0><tr><td>Start Time</td><td>%s</td></tr>\n", cp);
		nowtime = time (&nowtime);
		cp = ctime (&nowtime);
		rip (cp);
		usprintf (s, "<tr><td>Current Time&nbsp;&nbsp;</td><td>%s</td></tr>\n", cp);

		usputs (s, "<tr><td>Server</td><td>TNOS/"VERSION" "OS"</td></tr></table><pre>\n\n\n</pre><H3>Current Counters</H3>\n");
		usprintf (s, "<table border><tr><td>URL Requests</td><td>&nbsp;%ld&nbsp;</td></tr>\n", ports[rq->index].requests);
		usprintf (s, "<tr><td>Home Page Hits</td><td>&nbsp;%ld&nbsp;</td></tr>\n", ports[rq->index].homehits);
		usprintf (s, "<tr><td>Status Page Hits&nbsp;</td><td>&nbsp;%ld&nbsp;</td></tr>\n", ports[rq->index].stathits);
#ifdef HTTPPBBS
		usprintf (s, "<tr><td>PBBS Hits</td><td>&nbsp;%ld&nbsp;</td></tr>\n", ports[rq->index].bbshits);
#endif
		usprintf (s, "<tr><td>Active Requests</td><td>&nbsp;%d&nbsp;</td></tr>\n</table>\n", HttpUsers);

#ifdef STATS_HTTP
		usputs (s, "<pre>\n\n\n</pre><HR><H3>General Statistics</H3><PRE>\n");
		doPBBScmd (rq, "stats h g");

		usputs (s, "<pre>\n\n\n</pre><HR><H3>Daily Statistics</H3><PRE>\n");
		doPBBScmd (rq, "stats h d");

		usputs (s, "<pre>\n\n\n</pre><HR><H3>Weekly Statistics</H3><PRE>\n");
		doPBBScmd (rq, "stats h w");

		usputs (s, "<pre>\n\n\n</pre><HR><H3>Monthly Statistics</H3><PRE>\n");
		doPBBScmd (rq, "stats h m");

		usputs (s, "<pre>\n\n\n</pre><HR><H3>Yearly Statistics</H3><PRE>\n");
		doPBBScmd (rq, "stats h y");
#endif

		usputs (s, "<pre>\n\n\n</pre><HR><H5>For further information on TNOS can be found at <A HREF=\"http://www.lantz.com/tnos\">TNOS Central</A></H5>\n</HTML>\n");
		goto quit0;
	}
#ifdef HTTPPBBS
http_bbs_entry:
	if (!strncmp (newcommand, "bbs", 3))	{
		ports[rq->index].bbshits++;
#ifdef STATS_HTTP
		STATS_addhttp (1);
#endif
		/* if no authentication, it fails */
		if (rq->passwd == NULLCHAR)	{
			goto failauthorize;
		} else {
			/* check the authentication, here */
			char *tmp64, *cp2, pathbuf[MBXLINE], *path = pathbuf;
			if ((tmp64 = base64ToStr(rq->passwd)) == NULLCHAR)
				goto failauthorize;
#if 0
			log (-1, "HTTP Authentication Test: '%s' - '%s'", rq->passwd, (tmp64) ? tmp64 : "unknown");
#endif
			if (tmp64[0] == ':' || (cp2 = strchr (tmp64, ':')) == NULLCHAR)
				goto failauthorize0;

			*cp2++ = 0;
			if((rq->privs = userlogin(tmp64,cp2,&path,MBXLINE,&rq->anony)) == -1 || (rq->privs & NO_HTTP_IP) || (rq->privs & EXCLUDED_CMD))	{
				/* invalid authentication */
failauthorize0:			free (tmp64);
failauthorize:			retval = 401;
				httpHeaders (HTTPhtml, "401 Unauthorized", rq, 0, "the TNOS PBBS");
				usputs (s, "<HEAD><TITLE>Authorization Failed</TITLE></HEAD>\n<BODY><H1>Authorization Failed</H1>\nThe TNOS server requires proper authorization for this URL. Either your browser does not perform authorization, or your authorization has failed.</BODY>\n");
				goto quit0;
			}
#ifdef STRICT_HTTPCALL
			if (HTTPstrict && !(rq->privs & SYSOP_CMD) && !iscall(tmp64))
				goto failauthorize0;
#endif
			if (rq->privs & WAS_ANONY)	{
				int ii, trans;
				char *cptr, *cptr2;

				ii = SOCKSIZE;
				trans = DTranslate; /* Save IP address translation state */
				DTranslate = 0;     /* Force output to be numeric IP addr*/
				if (getpeername(s,(char *)&fsocket,&ii) != -1)	{
					cptr = psocket(&fsocket);
					if ((cptr2 = strchr(cptr, ':')) != NULLCHAR)
						*cptr2 = 0;
					if (strcmp(inet_ntoa(NonSecureAmpr), cptr) && (!*cptr || !strnicmp (cptr, "44.", 3) || !strnicmp (cptr, "127.0.0.1", 9)))	{
						if (MbAmprPerms)
							rq->privs = MbAmprPerms;
					} else if (MbNonAmprPerms)
						rq->privs = MbNonAmprPerms;
				} else if (MbNonAmprPerms)
					rq->privs = MbNonAmprPerms;
				DTranslate = trans;             /* Restore original state */
				if (MbHttpPerms)
					rq->privs = MbHttpPerms;
				rq->privs |= WAS_ANONY;
			}
			rq->username = strdup (tmp64);
			(void) strlwr (rq->username);
			rq->bbspath = strdup (pathbuf);
			free (tmp64);
		}
		/* passed authentication - now check for special URLs */
		
		/* if URL is 'bbs' or 'bbs/', then change it to 'bbs/${username}.html' */
		if (strlen(newcommand) == 3 || !strcmp (newcommand, "bbs/"))	{
			retval = 302;
			sprintf (rq->buf, "bbs/%s.%s", rq->username, HTMLEXT);
			free (rq->url);
			rq->url = strdup (rq->buf);
		  	httpHeaders (HTTPhtml, "302 Found", rq, 0, NULLCHAR);
		  	if (!head)
				usprintf (s, "<TITLE>Document moved</TITLE><H1>Document moved</H1>Please,"
				   "\n<A HREF=\"http://%s/%s\">click here</A>\n",
				   rq->hostname,rq->url);
			goto quit0;
		}

		/* if URL is 'bbs/area', then change it to 'bbs/area/${username}.html' */
		if (!strcmp (newcommand, "bbs/area"))	{
			retval = 302;
			sprintf (rq->buf, "bbs/area/%s.%s", rq->username, HTMLEXT);
			free (rq->url);
			rq->url = strdup (rq->buf);
		  	httpHeaders (HTTPhtml, "302 Found", rq, 0, NULLCHAR);
		  	if (!head)
				usprintf (s, "<TITLE>Document moved</TITLE><H1>Document moved</H1>Please,"
				   "\n<A HREF=\"http://%s/%s\">click here</A>\n",
				   rq->hostname, rq->url);
			goto quit0;
		}

		/* if URL is 'bbs/message/areaname', then change it to 'bbs/area/areaname.html' */
		if (!strncmp (newcommand, "bbs/message/", 12) && !strstr (newcommand, ".htm"))	{
			cp = &newcommand[12];
			sprintf (rq->buf, "bbs/area/%s.%s", cp, HTMLEXT);
			free (rq->url);
			rq->url = strdup (rq->buf);
			retval = 302;
		  	httpHeaders (HTTPhtml, "302 Found", rq, 0, NULLCHAR);
		  	if (!head)
				usprintf (s, "<TITLE>Document moved</TITLE><H1>Document moved</H1>Please,"
				   "\n<A HREF=\"http://%s/%s\">click here</A>\n",
				   rq->hostname, rq->url);
			goto quit0;
		}

		/* if URL is 'bbs/$(username}.html', then build it */
		sprintf (rq->buf, "bbs/%s.%s", rq->username, HTMLEXT);
		if (!strcmp (rq->buf, newcommand))	{
			/* special URL for user's login */
			rq->isvolatile = 1;
		  	httpHeaders (HTTPhtml, "200 Document follows", rq, 1, NULLCHAR);
		  	addUserFile (rq);
			goto quit0;
		}

		/* if URL is 'bbs/area/${areaname}.html', then build an area feature page */
		if (!strncmp (newcommand, "bbs/area/", 9) && !strchr(&newcommand[9], '/') && strstr (newcommand, ".htm"))	{
			char *cp2;
			/* special URL for area listing */
		  	strncpy (rq->buf, &newcommand[9], PLINELEN + 16);
		  	cp2 = strstr (rq->buf, ".htm");
		  	if (cp2)
				*cp2 = 0;
		  	rq->areaname = strdup (rq->buf);

		  	if (!checkareaperms (rq))
		  		goto failauthorize;
		  	
			doPBBScmd_init (rq);
			rq->msgcount = rq->m->nmsgs;
			rq->newmsgcount = rq->m->newmsgs;
			rq->msgcurrent = rq->m->current;

			rq->isvolatile = 1;
		  	httpHeaders (HTTPhtml, "200 Document follows", rq, 1, NULLCHAR);
			sprintf (rq->buf, "%s/bbs/areaopt.%s", rq->dirname, HTMLEXT);
		  	addMsgFile (rq->buf, rq);
			goto quit0;
		}

		/* if URL is 'bbs/area/areaname/xxx.html', then build an area list, starting w/msg#xxx */
		if (!strncmp (newcommand, "bbs/area/", 9))	{
			char *cp2, *cp3;
			/* special URL for area listing */
		  	strncpy (rq->buf, &newcommand[9], PLINELEN + 16);
		  	if ((cp2 = strchr (rq->buf, '/')) == NULLCHAR)
		  		goto failauthorize;
		  	*cp2++ = 0;
		  	rq->areaname = strdup (rq->buf);

		  	if (!checkareaperms (rq))
		  		goto failauthorize;
		  	
		  	if ((cp3 = strchr (cp2, '.')) == NULLCHAR)
		  		goto failauthorize;
		  	*cp3 = 0;

		  	rq->msgnum = atoi(cp2);
		  	if (!rq->msgnum && !strcmp (cp2, "new"))
		  		rq->msgnum = -1;
		  	
			doPBBScmd_init (rq);
			rq->msgcount = rq->m->nmsgs;
			rq->newmsgcount = rq->m->newmsgs;
			rq->msgcurrent = rq->m->current;

			rq->isvolatile = 1;
		  	httpHeaders (HTTPhtml, "200 Document follows", rq, 1, NULLCHAR);

			strcpy (rq->m->line, "L");
			if (rq->msgnum != -1)
				sprintf (&rq->m->line[1], " %d %d", rq->msgnum, rq->msgnum + 99);

			sprintf (rq->buf, "%s/bbs/arealist.%s", rq->dirname, HTMLEXT);
		  	addMsgFile (rq->buf, rq);
			goto quit0;
		}

		/* if URL is 'bbs/message/areaname/xxx.html', then build an message page */
		if (!strncmp (newcommand, "bbs/message/", 12))	{
			char *cp2, *cp3;
			/* special URL for reading messages */
			strncpy (rq->buf, &newcommand[12], PLINELEN + 16);
		  	if ((cp2 = strchr (rq->buf, '/')) == NULLCHAR)
		  		goto failauthorize;
		  	*cp2++ = 0;
		  	rq->areaname = strdup (rq->buf);

		  	if (!checkareaperms (rq))
		  		goto failauthorize;
		  	
		  	if ((cp3 = strchr (cp2, '.')) == NULLCHAR)
		  		goto failauthorize;
		  	*cp3 = 0;
			rq->msgnum = atoi (cp2);

			doPBBScmd_init (rq);
			rq->msgcount = rq->m->nmsgs;
			rq->newmsgcount = rq->m->newmsgs;
			rq->msgcurrent = rq->m->current;

			rq->isvolatile = 1;
		  	httpHeaders (HTTPhtml, "200 Document follows", rq, 1, NULLCHAR);

			sprintf (rq->m->line, "%d", rq->msgnum);
			sprintf (rq->buf, "%s/bbs/msglist.%s", rq->dirname, HTMLEXT);
		  	addMsgFile (rq->buf, rq);
			goto quit0;
		}

		/* if URL is 'bbs/cmd/xxxx', then build an text page from the BBS output of 'xxxx' */
		if (HTTPexecbbs && !strncmp (newcommand, "bbs/cmd/", 8))	{
			char *cp2, *cp3;

			cp2 = &newcommand[8];
			if (!strncmp (cp2, "area?", 5))	{
				cp3 = strchr (&cp2[5], '/');
				if (cp3 != NULLCHAR)	{
					cp2 += 5;
					*cp3++ = 0;
					rq->areaname = strdup (cp2);
					cp2 = cp3;
				}
			}
			while ((cp3 = strchr (cp2, '?')) != NULLCHAR)
		  		*cp3 = ' ';

			rq->isvolatile = 1;
		  	httpHeaders (HTTPplain, "200 Document follows", rq, 0, NULLCHAR);

			doPBBScmd (rq, cp2);
			goto quit0;
		}

		/* if URL is 'bbs/sendmail', then build an email message from the posting */
		if (!strcmp (rq->method, "POST") && posted_message && !strcmp (newcommand, "bbs/sendmail"))	{
			char *tmp2, *tmp3, *tmp4, msgtype = 'P', specialchar[3];
			char *subject = NULLCHAR;
			char fromaddr[128];
			char *toaddr = NULLCHAR;
			FILE *postfp;
			
			if (rq->privs & (NO_HTTP_MAIL | NO_SENDCMD))	{
				retval = 403;
				httpHeaders (HTTPhtml, "403 Forbidden", rq, 0, NULLCHAR);
				usputs (s, "<HEAD><TITLE>403 Post Forbidden</TITLE></HEAD>\n<BODY><H1>403 Post Forbidden</H1>\nThe TNOS server has rejected your posting.<p>\nYou do not have permission to post to this site.\n");
				sprintf (fromaddr, "%s/bbs/sendfail.%s", HTTPdir, HTMLEXT);
				if ((postfp = fopen (fromaddr, "r")) != NULLFILE)	{
					sendhtml (postfp, 1, rq);
					(void) fclose (postfp);
				} else
					usputs (s, "</BODY>\n");

				goto quit0;
			}

			postfp = tmpfile ();
			if (postfp == NULLFILE)
				goto post_response;	/* shouldn't happen */

			while ((cp = strchr (poststr, '+')) != NULLCHAR)
				*cp = ' ';
			
			for (cp = poststr; cp != NULLCHAR; cp = tmp4)	{
				tmp4 = strchr (cp, '&');
				if (tmp4)
					*tmp4++ = 0;
				if ((tmp2 = strchr (cp, '=')) == NULLCHAR)
					continue;
				*tmp2++ = 0;
				if (!strcasecmp (cp, "address"))	{
					toaddr = decode(tmp2);
					continue;
				} else if (!strcasecmp (cp, "subject"))	{
					subject = decode(tmp2);
					continue;
				} else if (!strcasecmp (cp, "type"))	{
					msgtype = *tmp2;
					continue;
				} else if (strncasecmp (cp, "line", 4))
					continue;

				/* This is a data line, add to the temp file */
				while ((tmp3 = strchr (tmp2, '%')) != NULLCHAR)	{
					*tmp3++ = 0;
					fputs (tmp2, postfp);
					specialchar[0] = *tmp3++;
					specialchar[1] = *tmp3++;
					specialchar[2] = 0;
					fputc (htoi (specialchar), postfp);
					tmp2 = tmp3;
				}
				if (*tmp2)
					fputs (tmp2, postfp);
				fputc ('\n', postfp);
				
			}
			if (toaddr)	{
				sprintf (fromaddr, "%s@%s", rq->username, rq->hostname);
				retval = 202;
				httpHeaders (HTTPhtml, "202 Accepted", rq, 0, NULLCHAR);
				usputs (s, "<HEAD><TITLE>202 Post Accepted</TITLE></HEAD>\n<BODY><H1>202 Post Accepted</H1>\nThe TNOS server has accepted and logged your posting.</BODY>\n");

				rewind (postfp);
				(void) rdaemon (postfp, NULLCHAR, fromaddr, toaddr, (subject) ? subject : "(no subject)", msgtype, 0);
				smtptick (NULL);
			} else {
				retval = 400;
			  	httpHeaders (HTTPhtml, "400 Bad Request", rq, 0, NULLCHAR);
				usputs (s, "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n<BODY><H1>400 Bad Request</H1>\nYour client sent a query that this server could not understand.<P>\nReason: No email address given.<P></BODY>\n");
			}

			(void) fclose (postfp);
			free (toaddr);
			free (subject);
			goto quit0;
		}

		/* otherwise, it must be a regular html file in the 'bbs' directory */
	}
#endif

	if (is_authorized (rq) == FALSE)	{
		/* this url is protected and proper authentication wasn't given */
		retval = 401;
		httpHeaders (HTTPhtml, "401 Unauthorized", rq, 0, rq->realm);
		usputs (s, "<HEAD><TITLE>Authorization Failed</TITLE></HEAD>\n<BODY><H1>Authorization Failed</H1>\nThe TNOS server requires proper authorization for this URL. Either your browser does not perform authorization, or your authorization has failed.</BODY>\n");
		goto quit0;
	}

	if (length == 0)
		sprintf(rq->buf,"%s/index.%s", rq->dirname, HTMLEXT);
	else	{
		if (rq->ftpdirname && !strncmp (newcommand, "ftp", 3))	{
			char *newercommand = newcommand;
			/* viewing/retrieving files from defined FTP directory */
			newercommand += 3;
			if (*newercommand && *newercommand == '/')
				newercommand++;
			sprintf(rq->buf, "%s/%s", rq->ftpdirname, newercommand);
			isValidFtpRequest = 1;
		} else
			sprintf(rq->buf,"%s/%s", rq->dirname, newcommand);
	}

	/* delete any .. to avoid */
	/* anyone telnetting to http and cd'ing to a directory */
	/* other than within Httpdir */

	for ( i=0; i <= (int) strlen(rq->buf); i++ ) {
		if (rq->buf[i] == '.' && rq->buf[i+1] == '.') {
			rq->buf[i] = ' ';
			rq->buf[i+1] = ' ';
		}
	}

	if (stat (rq->buf, &sb) != -1)
		isdir = sb.st_mode & S_IFDIR;
	if (rq->buf[strlen(rq->buf)-1] == '/') {
		strcat (rq->buf, "welcome." HTMLEXT);
		if (isValidFtpRequest || !access (rq->buf, 0))	{
			retval = 200;
		  	httpHeaders (HTTPhtml, "200 Document follows", rq, 0, NULLCHAR);
		  	if (!head)
				err = mkwelcome (rq->dirname, rq->buf, isValidFtpRequest, rq->ftpdirname);
			goto quit0;
		}
	} else if (isdir) {
		retval = 302;
		strncpy (rq->buf, newcommand, PLINELEN + 15);
		strcat (rq->buf, "/");
		free (rq->url);
		rq->url = strdup (rq->buf);
	  	httpHeaders (HTTPhtml, "302 Found", rq, 0, NULLCHAR);
	  	if (!head)
			tprintf ("<TITLE>Document moved</TITLE><H1>Document moved</H1>Please,"
			   "\n<A HREF=\"http://%s/%s\">click here</A>\n",
			   rq->hostname, rq->url);
		goto quit0;
	} else
		fp = fopen (rq->buf, READ_BINARY);

	if (fp != NULLFILE)	{
		int thetype = -1;

		if ((cp = strrchr (rq->buf, '.')) != NULLCHAR)	{
			cp++;
			for (thetype = HTTPhtml; HTTPtypes[thetype].ext != NULLCHAR; thetype++) {
				if (!strnicmp (HTTPtypes[thetype].ext, cp, strlen(HTTPtypes[thetype].ext)))
					break;
			}
		}
		if (thetype == -1 || HTTPtypes[thetype].type1 == -1)
			/* either had no extension, or extension not found in table */
			thetype = (isbinary(fp)) ? HTTPbinary : HTTPplain;

		retval = 200;
		(void) fstat (fileno(fp), &sb);
		if (!head && rq->newcheck && !isnewer (sb.st_mtime, rq->newcheck))	{
			head = 1;
			retval = 304;
		}

		if (!strnicmp (newcommand, "bbs/user/", 9))
			rq->isvolatile = 1;
	  	httpHeaders (thetype, (retval == 304) ? "304 Not Modified" : "200 Document follows", rq, 1, NULLCHAR);
	  	httpFileinfo (fp, rq, thetype, version);
		if (!head)	{
			if (thetype != HTTPhtml && thetype != HTTPhtm)	{	/* if not html, just send */
				tprintf ("\n");
				(void) sendfile (fp, s, IMAGE_TYPE, 0);
			} else				/* otherwise, scan for server-side includes */
				sendhtml (fp, 1, rq);
		}
		(void) fclose (fp);
	} else if (err) {
		retval = 404;
		httpHeaders (HTTPhtml, "404 Not Found", rq, 0, NULLCHAR);
		if (!head)
			tprintf ("<TITLE>Error</TITLE><H1>404 Not Found</H1>The file \"/%s\" does not exist on this server.\n", newcommand);
	}
quit0:
	free (tmp);
	openHTTPlog ("http");
	log (s, "port #%u: \"%s\" %d", port, command, retval);
	closeHTTPlog();
	(void) countusage ("tcount.dat", rq->dirname, 0, 1);
 
quit:
        usflush(Curproc->output);
	kpause (1000);
	close_s (Curproc->output);

	doPBBScmd_exit (rq);
	free (rq->url);
	free (rq->method);
	free (rq->version);
	free (rq->newcheck);
	free (rq->from);
	free (rq->referer);
	free (rq->agent);
	free (rq->passwd);
	free (rq->usetime);
	free (rq->useerror);
	free (rq->realm);
	free (rq->cookie);
	free (rq->rmtaddr);
	free (rq->rmthost);
	
#ifdef HTTPPBBS
	free (rq->areaname);
	free (rq->username);
	free (rq->bbspath);
#endif
	free (rq);

	HttpUsers--;
	HttpSessions--;
	ksignal (&HttpSessions, 1);
	kwait (NULL);
}



static char *
gmt_ptime (time_t *thetime)
{
	return ht_time(*thetime,HTTP_TIME_FORMAT, 1);
}



static char *
ht_time (time_t t, const char *fmt, int gmt)
{
static char ts[LINELEN];
struct tm *tms;

	tms = (gmt ? gmtime(&t) : localtime(&t));

	/* check return code? */
	(void) strftime (ts, LINELEN, fmt, tms);
	return ts;
}



static void
httpFileinfo (FILE *fp, struct reqInfo *rq, int thetype, char *vers)
{
struct stat sb;

	if (vers == NULLCHAR)
		return;		/* it is 0.9, no headers sent */
	if (!rq->isvolatile)
		if (fstat (fileno(fp), &sb) != -1)
			tprintf ("%s %s", HTTPHdrs[HDR_MODIFIED], gmt_ptime(&sb.st_mtime));

	if (thetype != HTTPhtml)
		tprintf ("%s %lu\n", HTTPHdrs[HDR_LENGTH], filelength(fileno(fp)));
}



static char *
assigncookie (struct reqInfo *rq, time_t thetime)
{
static char cookie[32];
char ipaddr[10], buf[32];
int i = SOCKSIZE;
struct sockaddr_in fsocket;
FILE *fp;
long nextCookie = 0;
int assignone = 1;
unsigned long address = INADDR_ANY;

	if (getpeername (rq->sock, (char *) &fsocket, &i) != -1)
		address = fsocket.sin_addr.s_addr;

	/* build ipaddress portion of cookie, which we'll also use to
	   lookup this site, and see if there is already an assigned cookie */
	sprintf (ipaddr, "%02x%02x%02x%02x", (int) ((address >> 24) & 255),
		(int) ((address >> 16) & 255), (int) ((address >> 8) & 255),
		(int) (address & 255));
	
	/* get the next cookie number we should use, and build cookie
	   string, in case we DO assign a new one */
	if ((fp = fopen (Cookiefile, READ_TEXT)) != NULLFILE)	{
		cookie[0] = 0;
		(void) fgets (cookie, 30, fp);
		nextCookie = atol (cookie);
		(void) fclose (fp);
	}
	sprintf (cookie, "%8.8s_%08ld_%012ld", ipaddr, ++nextCookie, thetime);

	/* look to see if there is already a cookie assigned to for this
	   site. Browsers that do NOT support cookies could cause the
	   Cookiedata file to grow to a large size, without this piece of code */
	if ((fp = fopen (Cookiedata, READ_TEXT)) != NULLFILE)	{
		while (fgets (buf, 32, fp) != NULLCHAR)	{
			if (!strncmp (buf, ipaddr, 8))	{
				strncpy (cookie, buf, 32);
				rip (cookie);
				assignone = 0;
				break;
			}
		}
		(void) fclose (fp);
	}
	
	
	if (assignone)	{	/* if we didn't find an old one, save this info */
		if ((fp = fopen (Cookiefile, WRITE_TEXT)) != NULLFILE)	{
			fprintf (fp, "%ld\n", nextCookie);
			(void) fclose (fp);
		} else	{
			/* if we can't save them, log an error, and disable creating more */
			log (-1, "Cannot write data to Cookiefile: '%s'", Cookiefile);
			HTTPusecookies = 0;
		}

		if ((fp = fopen (Cookiedata, APPEND_TEXT)) != NULLFILE)	{
			fprintf (fp, "%s\n", cookie);
			(void) fclose (fp);
		}
	}

	return (cookie);
}



static void
httpHeaders (int type, const char *str, struct reqInfo *rq, int more, const char *realm)
{
time_t thetime;

	if (rq->version == NULLCHAR)
		return;		/* it is 0.9, no headers sent */
	(void) time (&thetime);
	
	tprintf ("HTTP/1.0 %s\n%s %s", str, HTTPHdrs[HDR_DATE], gmt_ptime(&thetime));
	if (rq->isvolatile)
		tprintf ("%s %s", HTTPHdrs[HDR_EXPIRES], gmt_ptime(&thetime));
	if (realm)
		tprintf ("%s basic realm=\"%s\"\n", HTTPHdrs[HDR_AUTHENTICATE], realm);
	tprintf ("%s 1.0\n%s TNOS/"VERSION" "OS"\n", HTTPHdrs[HDR_MIME], HTTPHdrs[HDR_SERVER]);
	tprintf ("%s http://%s/%s\n", HTTPHdrs[HDR_LOCATION], rq->hostname, rq->url);

	if (HTTPusecookies && (rq->cookie == NULLCHAR || !strstr (rq->cookie, "TNOS=")))
		tprintf ("Set-Cookie: TNOS=%s; expires=%s; path=/; domain=%s\n",
			assigncookie (rq, thetime), ht_time (thetime + SECS_IN_6MONTHS, DEFAULT_TIME_FORMAT, 1), rq->hostname);

	tprintf ("%s %s%s\n", HTTPHdrs[HDR_TYPE], CTypes1[HTTPtypes[type].type1], HTTPtypes[type].type2);

	/* must have this line, when no more headers needed */
	if (more == 0)
		tputs ("\n");
}



static int
mkwelcome (const char *Hdir, char *file, int ftpfile, const char *Ftpdir)
{
char *dirstring,*p,*cp;
const char *ccp, *bitmap;
struct ffblk ffblk;
int done,pre = 0, k;
char *updir = NULLCHAR;
char *endstr;
int isadir;

	if ((dirstring = (char *)mallocw (strlen(file) + 16)) == NULL)
		return 0;

	strcpy (dirstring, file);
	(void) addFile (dirstring, Curproc->output);

	cp = strrchr (dirstring, '/');
	if (cp)
		*++cp = '\0';
	if (!ftpfile && strlen(Hdir) != strlen(dirstring))	{
		updir = strdup(&dirstring[strlen(Hdir)]);
		p = strrchr (updir, '/');
		if (p)
			*p = 0;
		p = strrchr (updir, '/');
		if (p == NULLCHAR)
			updir[0] = 0;
		else
			*++p = 0;
	} else if (ftpfile && strlen(Ftpdir) != strlen(dirstring))	{
		updir = strdup(&dirstring[strlen(Ftpdir)]);
		p = strrchr (updir, '/');
		if (p)
			*p = 0;
		p = strrchr (updir, '/');
		if (p == NULLCHAR)
			updir[0] = 0;
		else
			*++p = 0;
	}
	
	endstr = &dirstring[strlen(dirstring)];

	strcat (dirstring, HTTPheadfile);
	pre = addFile (dirstring, Curproc->output);

	strcpy (endstr, WILDCARD);
	done = findfirst (dirstring, &ffblk, (FA_HIDDEN|FA_SYSTEM|FA_DIREC));
	if (done)	{
		free (dirstring);
		return -1;
	}
	*endstr = 0;
	
	(void) sockmode (Curproc->output, SOCK_ASCII);
	(void) setflush (Curproc->output, -1); /* we will do our own flushing */
	p = dirstring + 1 + ((ftpfile) ? strlen(Ftpdir) : strlen(Hdir));
	if (ftpfile)	{
		p = (char *) mallocw (strlen (p) + 5);
		sprintf (p, "ftp/%s", dirstring + 1 + ((ftpfile) ? strlen(Ftpdir) : strlen(Hdir)));
	}
	undosify (p);
	if (!pre)
		tprintf ("<HEAD><TITLE>Index of /%s</TITLE></HEAD></BODY><H1>Index of /%s</H1>\n", p, p);
	tputs ("<PRE>\n");

	/* include the 'include' file, if it exists in this directory */
	strcpy (endstr, HTTPinclude);
	if (addFile (dirstring, Curproc->output))
		tputs ("<P>\n");
	*endstr = 0;

	tputs ("<IMG SRC=\"/icons/blank.xbm\" ALT=\"      \"> Name                   Last modified     Size\n<HR>\n");
	if (updir != NULLCHAR)	{
		if (*updir)
			tprintf ("<IMG SRC=\"/icons/menu.xbm\" ALT=\"[DIR ]\"> <A HREF=\"%s%s\">Parent Directory</a>\n", (ftpfile) ? "/ftp" : "", updir);
		free (updir);
	}
	while (!done) {
		isadir = 0;

		if (ffblk.ff_name[0] != '.')	{
			if (ffblk.ff_attrib & FA_DIREC)	{
				ccp = "[DIR ]";
				bitmap = "menu";
				isadir = 1;
			} else {
				ccp = "[FILE]";
				bitmap = "text";
				k = (int) (strlen(ffblk.ff_name) - 4);
				if (k > 0 && (!strnicmp (&ffblk.ff_name[k], ".gif", 4) || !strnicmp (&ffblk.ff_name[k], ".jpg", 4) || !strnicmp (&ffblk.ff_name[k], ".xbm", 4)))
					bitmap = "image";
			}
			tprintf (entry, bitmap, ccp);
			tprintf (entry1, p, ffblk.ff_name, (isadir) ? "/" : "", ffblk.ff_name);
			for (k = (int) strlen (ffblk.ff_name); k < 22; k++)
				tputc (' ');
			tprintf (entry2,
#ifdef MSDOS
				(ffblk.ff_fdate) & 0x1f,	/* day */
				Months[((ffblk.ff_fdate >> 5) & 0xf)-1], /* month */
				(ffblk.ff_fdate >> 9) + 80,	/* year */
				(ffblk.ff_ftime >> 11) & 0x1f,	/* hour */
				(ffblk.ff_ftime >> 5) & 0x3f,	/* minutes */
#else
				ffblk.ff_ftime.tm_mday,		/* day */
				Months[ffblk.ff_ftime.tm_mon],	/* month */
				ffblk.ff_ftime.tm_year,		/* year */
				ffblk.ff_ftime.tm_hour,		/* hour */
				ffblk.ff_ftime.tm_min,		/* minute */
#endif
				(int)((ffblk.ff_fsize + 1023L) / 1024L));
		}
		kwait (NULL);
		done = findnext (&ffblk);
	}
	tputs ("</PRE>\n");
	usflush (Curproc->output);

	strcat (dirstring, HTTPtailfile);
	if (!addFile (dirstring, Curproc->output))
		tputs ("</BODY>\n");
	free (dirstring);
	if (ftpfile)
		free (p);
	return 0;
}


extern FILE *Logfp;



static void
openHTTPlog (const char *name)
{
char filename[256];

	HTTPsavedfp = Logfp;
	sprintf (filename, "%s/%s.log", LOGdir, name);
	Logfp = fopen (filename, APPEND_TEXT);
}



static void
closeHTTPlog (void)
{
	if (Logfp != NULLFILE)
		(void) fclose (Logfp);
	Logfp = HTTPsavedfp;
}



static int
getmonth (char *cp)
{
int k;

	for (k = 0; k < 12; k++)	{
		if (!strnicmp (cp, Months[k], 3))	{
			return k;
		}
	}
	return 0;
}



static long
countusage (const char *file, const char *basedir, int display, int increase)
{
char name[256], buf[32];
FILE *fp;
long count = 0;

	sprintf (name,"%s/counts/%s", basedir, file);
	if ((fp = fopen (name, READ_TEXT)) != NULLFILE) {
		(void) fgets (buf, sizeof(buf), fp);
		(void) fclose (fp);
		count = atol (buf);
	}
	if (increase)	{
		count++;
		if ((fp = fopen (name, WRITE_TEXT)) != NULLFILE) {
			fprintf (fp, "%ld", count);
			(void) fclose (fp);
		}
	}
	if (display)
		tprintf ("%ld", count);
	return (count);
}



static int
isnewer (time_t thetime, char *tmstr)
{
time_t new;
struct tm t, *ltm;
char *cp, *cp2;
int format = 0;
time_t offset;

	/* the string should be in one of these three formats:
	 * (a) RFC 822 (preferred) - Sun, 06 Nov 1994 08:49:37 GMT
	 * (b) RFC 850		   - Sunday, 06-Nov-94 08:49:37 GMT
	 * (c) ANSI C's asctime    - Sun Nov  6 08:49:37 1994
	 */
	/* skip past the name of the weekday */
	cp = skipwhite (tmstr);
	cp = skipnonwhite (cp);
	cp = skipwhite (cp);
	if (isdigit (*cp))	{	/* format a or b */
		cp[2] = 0;
		t.tm_mday = atoi (cp);
		cp += 3;
		t.tm_mon = getmonth (cp);
		cp += 4;
		cp2 = skipnonwhite (cp);
		*cp2 = 0;
		t.tm_year = atoi (cp);
		cp = ++cp2;
	} else {			/* format c */
		t.tm_mon = getmonth (cp);
		cp += 4;
		t.tm_mday = atoi (cp);
		cp += 3;
		format = 1;
	}

	/* time parsing is common between the three formats */
	sscanf (cp, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec);
	/* then parse the year, if format c */
	if (format) {
		cp += 9;
		t.tm_year = atoi (cp);
	}
	if (t.tm_year > 1900)
		t.tm_year -= 1900;
#ifdef HAVE_TM_ISDST
	t.tm_isdst = -1;
#endif
	new = mktime (&t);
	ltm = localtime(&new);		/* to get timezone info */
#if defined(HAVE_TM_ISDST) && !defined(MSDOS)
	offset = ((timezone - (ltm->tm_isdst * 3600L)) * -1);
#else
	offset = (ltm->tm_gmtoff * -1);
#endif
	new += offset;
	return (new != thetime);
}



static void
send_size (int sizefmt, long size)
{
	if (sizefmt == SIZEFMT_BYTES)	{
		tprintf ("%ld", size);
		return;
	}
	if (size == -1)
		tputs ("    -");
	else {
		if (!size)
			tputs ("   0K");
		else if (size < 1024)
			tputs ("   1K");
		else if (size < 1048576)
			tprintf ("%4ldK", size / 1024L);
		else
			tprintf ("%4ldM", size / 1048576L);
	}
}



#ifdef HTTPCGI
static void
setup_cgi_variables (struct reqInfo *rq, char *name)
{
char buf[80], *cp;
char *scriptname;


	if (name == NULLCHAR)
		scriptname = strdup (rq->url);
	else
		scriptname = name;
	(void) setenv ("GATEWAY_INTERFACE", "CGI/1.1", 0);
	(void) setenv ("SERVER_NAME", rq->hostname, 0);
	(void) setenv ("SERVER_SOFTWARE", "TNOS/"VERSION" "OS, 0);
	(void) setenv ("SERVER_PROTOCOL", "HTTP/1.0", 0);
	sprintf (buf, "%d", rq->port);
	(void) setenv ("SERVER_PORT", buf, 1);
	(void) setenv ("DOCUMENT_ROOT", rq->dirname, 1);
	(void) setenv ("REQUEST_METHOD", rq->method, 1);
	(void) setenv ("REMOTE_ADDR", (rq->rmtaddr) ? rq->rmtaddr : "", 1);
	(void) setenv ("REMOTE_HOST", (rq->rmthost) ? rq->rmthost : "", 1);
	(void) setenv ("SCRIPT_NAME", scriptname, 1);
	(void) setenv ("HTTP_REFERER", (rq->referer) ? rq->referer : "", 1);
	(void) setenv ("HTTP_USER_AGENT", (rq->agent) ? rq->agent : "", 1);

	if ((cp = strchr (scriptname, '?')) != NULLCHAR)	{
		*cp++ = 0;
		(void) setenv ("QUERY_STRING", cp, 1);
	} else
		(void) setenv ("QUERY_STRING", "", 1);

	sprintf (buf, "sysop@%s", rq->hostname);
	(void) setenv ("SERVER_ADMIN", buf, 0);
	
	/* not completely to spec */
	sprintf (buf, "%s/%s", rq->dirname, scriptname);
	(void) setenv ("SCRIPT_FILENAME", buf, 1);

	/* not supported, yet:
		PATH_INFO, PATH_TRANSLATED, AUTH_TYPE
		REMOTE_USER, REMOTE_IDENT, CONTENT_TYPE,
		CONTENT_LENGTH, HTTP_FROM, HTTP_ACCEPT
	 */

	if (scriptname != name)
		free (scriptname);
}
#endif



static void
sendhtml (FILE *fp, int firstfile, struct reqInfo *rq)
{
time_t t;
struct stat ft;
char *cp, *cp1, *cp2, c;
char *remainder, *cmdname, newname[256];
FILE *tfp;
char *bufptr;
char buf[PLINELEN + 16];
int c2;
struct tag *tg;
int val;


#ifdef HTTPCGI
	setup_cgi_variables (rq, NULLCHAR);
#endif
	(void) sockmode (Curproc->output, SOCK_BINARY);

	if (firstfile)	{	/* if first file (not included file), search for meta tags */
	    	while ((c2 = fgetc (fp)) != EOF) {
			if (c2 == '<')	{
				tg = parse_tag (fp);
				if (tg != NULLTAG && !strcmp (tg->name, "meta"))	{
					/* found a meta tag */
					cp1 = find_option (tg, "http-equiv", NULL);
					if (cp1 != NULLCHAR)	{
			
						cp2 = find_option (tg, "content", NULL);
						tprintf ("%s: %s\n", cp1, (cp2) ? cp2 : "");
						free (cp1);
						free (cp2);
					}
				}
				delete_tag (tg);
			}
		}
		clearerr (fp);
		rewind (fp);
		tprintf ("\n");
	}

    	while ((fgets (buf, PLINELEN + 15, fp)) != NULLCHAR) {
		kwait(NULL);
		bufptr = buf;
		while (bufptr  && (cp1 = strstr (bufptr, "<!--#")) != NULLCHAR) {
			if (cp1 != bufptr)	{
				*cp1 = 0;
				tputs (bufptr);
			}
			cp1 += 5;
			remainder = strstr (cp1, "-->");
			if (remainder != NULLCHAR) {
				*remainder = 0;
				remainder = &remainder[3];
			}
			cmdname = skipwhite (cp1);
			cp1 = skipnonwhite (cmdname);
			cp1 = skipwhite (cp1);

			if (!strnicmp (cmdname, "echo", 4)) {
				if (!strnicmp (cp1, "var=\"", 5)) {
					(void) time (&t);
					cp1 += 5;
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					if (!stricmp (cp1, "DATE_LOCAL"))
						tputs (ht_time (t, rq->usetime, 0));
					else if (!stricmp (cp1, "DATE_GMT"))
						tputs (ht_time (t, HTTP_TIME_FORMAT, 1));
					else if (!stricmp (cp1, "HOSTNAME"))
						tputs ((rq->hostname) ? rq->hostname : "unknownhost");
					else if (!stricmp (cp1, "DOCUMENT_URI"))
						tprintf ("/%s", rq->url);
					else if (!stricmp (cp1, "VERSION"))
						tputs (Version);
#ifdef HTTPPBBS
					else if (!stricmp (cp1, "USERNAME"))
						tputs ((rq->username) ? rq->username : "unknownUser");
					else if (!stricmp (cp1, "AREANAME"))
						tputs ((rq->areaname) ? rq->areaname : "unknownArea");
					else if (!stricmp (cp1, "MESSAGECOUNT"))
						tprintf ("%d", rq->msgcount);
					else if (!stricmp (cp1, "NEWMSGCOUNT"))
						tprintf ("%d", rq->newmsgcount);
					else if (!stricmp (cp1, "CURMESSAGE"))
						tprintf ("%d", rq->msgcurrent);
					else if (!stricmp (cp1, "MESSAGENUM"))
						tprintf ("%d", rq->msgnum);
					else if (!stricmp (cp1, "PREV_MSGNUM"))
						tprintf ("%d", rq->msgnum - 1);
					else if (!stricmp (cp1, "NEXT_MSGNUM"))
						tprintf ("%d", rq->msgnum + 1);
					else if (!stricmp (cp1, "PERMISSIONS"))
						doPBBScmd (rq, "security");
					else if (!stricmp (cp1, "PREV_LIST_START"))
						tprintf ("%d", (rq->msgnum > 100) ? rq->msgnum - 100 : 1);
					else if (!stricmp (cp1, "NEXT_LIST_START"))
						tprintf ("%d", rq->msgnum + 100);
					else if (!stricmp (cp1, "PREV_LIST_BUTTON"))	{
						if (rq->msgnum > 1)	{
							sprintf (rq->buf, "%s/bbs/plbutton.cat", rq->dirname);
							if ((tfp = fopen (rq->buf, "r")) != NULLFILE) {
								sendhtml (tfp, 0, rq);
								(void) fclose (tfp);
							}
						}
					} else if (!stricmp (cp1, "PREV_MSG_BUTTON"))	{
						if (rq->msgnum > 1)	{
							sprintf (rq->buf, "%s/bbs/pmbutton.cat", rq->dirname);
							if ((tfp = fopen (rq->buf, "r")) != NULLFILE) {
								sendhtml (tfp, 0, rq);
								(void) fclose (tfp);
							}
						}
					} else if (!stricmp (cp1, "NEXT_MSG_BUTTON"))	{
						if (rq->msgcount > rq->msgnum)	{
							sprintf (rq->buf, "%s/bbs/nmbutton.cat", rq->dirname);
							if ((tfp = fopen (rq->buf, "r")) != NULLFILE) {
								sendhtml (tfp, 0, rq);
								(void) fclose (tfp);
							}
						}
					} else if (!stricmp (cp1, "NEXT_LIST_BUTTON"))	{
						if (rq->msgcount > rq->msgnum + 100)	{
							sprintf (rq->buf, "%s/bbs/nlbutton.cat", rq->dirname);
							if ((tfp = fopen (rq->buf, "r")) != NULLFILE) {
								sendhtml (tfp, 0, rq);
								(void) fclose (tfp);
							}
						}
					} else if (!stricmp (cp1, "AREA_LIST") || !stricmp (cp1, "AREA_MSG"))
						doPBBScmd_term (rq);
#endif
#ifdef ALLSERV
					else if (!stricmp (cp1, "QUOTE")) {
						cp2 = getquote ();
						tputs ((cp2) ? cp2 : "[quote not found]");
					}
#endif
					else if (!stricmp (cp1, "DOCUMENT_NAME"))	{
						if (rq->url)	{
							cp2 = strrchr (rq->url, '/');
							if (cp2)
								cp2++;
							else
								cp2 = rq->url;
							tputs (cp2);
						}
					} else if (!stricmp (cp1, "LAST_MODIFIED")) {
						(void) fstat (fileno(fp), &ft);
						tputs (ht_time (ft.st_mtime, rq->usetime, 0));
					} else if (!stricmp (cp1, "TOTAL_HIT_COUNT") || !stricmp (cp1, "TOTAL_HITS"))
						(void) countusage ("tcount.dat", rq->dirname, 1, 0);
					else if (!stricmp (cp1,"REQ_FROM")) {
						if (rq->from)
							tputs(rq->from);
					} else if (!stricmp (cp1,"REQ_REFERER")) {
						if (rq->referer)
							tputs (rq->referer);
					} else if (!stricmp (cp1,"REQ_AGENT")) {
						if (rq->agent)
							tputs (rq->agent);
					} else if (!stricmp (cp1,"REQ_COOKIE")) {
						if (rq->cookie)
							tputs (rq->cookie);
					} else if ((cp = getenv (cp1)) != NULLCHAR)
						tputs (cp);
					else
						tputs (rq->useerror);
				} else if (!strnicmp (cp1, "count=\"", 7) || !strnicmp (&cp1[1], "count=\"", 7))	{
					c = (char) tolower(*cp1);
					switch (c) {
						case 'c':	/* count */
						case 'i':	/* icount */
						case 's':	/* scount */
								cp1 = strchr (cp1, '\"');
								if (cp1)	{
									cp2 = strrchr (++cp1, '\"');
									if (cp2)
										*cp2 = 0;
									(void) countusage (cp1, rq->dirname, (c != 's'), (c != 'c'));
								}
								break;
						default:	tputs (rq->useerror);

					}
				} else
					tputs (rq->useerror);
			} else if (!strnicmp (cmdname, "config", 6)) {
				if (!strnicmp (cp1, "timefmt=\"", 9)) {
					cp1 += 9;
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					free (rq->usetime);
					rq->usetime = strdup (cp1);
				} else if (!strnicmp (cp1, "sizefmt=\"", 9)) {
					c = (char) tolower(cp1[9]);
					/* either 'abbrev' or 'bytes' */
					rq->sizefmt = (c == 'a') ? SIZEFMT_KMG : SIZEFMT_BYTES;
				} else if (!strnicmp (cp1, "errmsg=\"", 8)) {
					cp1 += 8;
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					free (rq->useerror);
					rq->useerror = strdup (cp1);					
				} else
					tputs (rq->useerror);
			} else if (!strnicmp (cmdname, "include", 7)) {
				file_or_virtual (cp1, newname, rq);
				if (*newname && (tfp = fopen (newname, "r")) != NULLFILE) {
					sendhtml (tfp, 0, rq);
					(void) fclose (tfp);
				} else
					tputs (rq->useerror);
			} else if (!strnicmp (cmdname, "fsize", 5)) {
				file_or_virtual (cp1, newname, rq);
				if (*newname)	{
					(void) stat (newname, &ft);
					send_size (rq->sizefmt, (long) ft.st_size);
				} else
					tputs (rq->useerror);
                        } else if (!strnicmp (cmdname, "flastmod", 8)) {
				file_or_virtual (cp1, newname, rq);
				if (*newname)	{
					(void) stat (newname, &ft);
					tputs (ht_time (ft.st_mtime, rq->usetime, 0));
				} else
					tputs (rq->useerror);
#if 0
			} else if (HTTPscript && !strnicmp (cmdname, "tscript", 7)) {
				/* to be added later */
				tputs (rq->useerror);
#endif
			} else if (!strnicmp (cmdname, "exec", 4))	{
				/* exec cgi no supported, at least not yet */
#ifdef HTTPPBBS
				if (HTTPexecbbs && !strnicmp (cp1, "bbs=\"", 5)) {
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					doPBBScmd (rq, &cp1[5]);
				} else
#endif
#ifdef HTTPCGI
				if (HTTPexeccgi && !strnicmp (cp1, "cgi=\"", 5)) {
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					if ((tfp = popen (&cp1[5], "r")) != NULLFILE) {
						while ((val = fgetc (tfp)) != EOF)
							tputc (uchar(val));
					}
					(void) pclose (tfp);
				} else
#endif
				if (HTTPexeccmd && !strnicmp (cp1, "cmd=\"", 5)) {
					if ((cp2 = strrchr (cp1, '\"')) != NULLCHAR)
						*cp2 = 0;
					if ((tfp = popen (&cp1[5], "r")) != NULLFILE) {
						while ((val = fgetc (tfp)) != EOF)
							tputc (uchar(val));
					}
					(void) pclose (tfp);
				} else
					tputs (rq->useerror);
			} else {
				tprintf ("<!--#%s-->", cmdname);
			}
			bufptr = remainder;
		}
		if (bufptr != NULLCHAR)
			tputs (bufptr);
	}
}



static void
file_or_virtual (char *cp, char *newname, struct reqInfo *rq)
{
char *cp2, *cp3;

	*newname = 0;
	if (!strnicmp (cp, "virtual=\"", 9))	{
		/* virtual filename, based on root http directory */
		cp2 = &cp[9];
		if (!strncmp (cp2, "../", 3) || !strncmp (cp2, "./", 2))
			cp2 = (strchr (cp2, '/') + 1);		/*lint !e613 * can't happen, see above line */
		sprintf (newname, "%s/%s", rq->dirname, cp2);
	} else if (!strnicmp (cp, "file=\"", 6)) {
		/* absolute filename */
		cp2 = &cp[6];
		if (!strncmp (cp2, "../", 3) || !strncmp (cp2, "./", 2))
			cp2 = (strchr (cp2, '/') + 1);		/*lint !e613 * can't happen, see above line */
		if (*cp2 == '/')
			cp2++;
		cp3 = strrchr (rq->url, '/');
		sprintf (newname, "%s%s/%s", rq->dirname, (cp3) ? cp3 : "", cp2);
	}
	if (*newname && (cp2 = strrchr (newname, '\"')) != NULLCHAR)
		*cp2 = 0;
}



static struct secureURL *
secured_url (struct reqInfo *rq)
{
struct secureURL *sec;

	/* lookup rq->url in securedURLS table, and return entry or NULL */
	for (sec = securedURLS; sec != NULLSECUREURL; sec = sec->next)	{
		/* 'http sec' url's ending in '/' protect entire directories */
		if (sec->url[strlen (sec->url) - 1] == '/')	{
			if (!strncmp (sec->url, rq->url, strlen (sec->url)))
				return sec;
		} else if (!strcmp (sec->url, rq->url))	/* single protected URL */
			return sec;
	}
	return NULLSECUREURL;
}



static int
is_authorized (struct reqInfo *rq)
{
int retval = FALSE;
struct secureURL *sec;
char *tmp64 = NULLCHAR, *cp2;

	/* See if URL is secured, if not return TRUE */
	if ((sec = secured_url (rq)) == NULLSECUREURL)
		return TRUE;
		
	rq->realm = strdup (sec->realm);
	/* this URL is secured */
	if (rq->passwd != NULLCHAR)	{
		/* check the authentication, here */
		if (((tmp64 = base64ToStr(rq->passwd)) != NULLCHAR) && ((cp2 = strchr (tmp64, ':')) != NULLCHAR))	{
			*cp2++ = 0;
			retval = http_authcheck (sec->pwfile, tmp64, cp2);
		}
		free (tmp64);
	}
	return retval;
}



static int
http_authcheck (char *filename, char *username, char *pass)
{
char buf[80];
char *cp = NULLCHAR;
char *cp1;
FILE *fp;
int retval = FALSE;

	if ((fp = fopen (filename, "r")) == NULLFILE)	/* Authorization file doesn't exist */
		log (-1, "Authorization file %s not found\n", filename);
	else	{
		while ((void) fgets (buf, sizeof(buf), fp), !feof (fp)) {
			if (buf[0] == '#')
				continue;	/* Comment */

			if ((cp = strchr (buf, ':')) == NULLCHAR)
				continue;	/* Bogus entry */

			*cp++ = '\0';		/* Now points to password */
			if (strcmp (username, buf) == 0)	{
				if ((cp1 = strchr (cp, ':')) == NULLCHAR)
					break;

				*cp1 = '\0';
				if (strcmp (cp, pass) != 0)
					break;	/* Password required, but wrong one given */

				retval = TRUE;	/* whew! finally made it!! */
				break;
			}
		}
		(void) fclose(fp);
	}
	return retval;
}

#endif /* HTTP */

#if defined(BROWSER) || defined(HTTP)

struct tag *
parse_tag (FILE *fp)
{
struct tag *tag;
struct options *opt;
int c, theindex = 0;
char buf[128];
int endquote;
int lookfor;

	tag = (struct tag *) callocw (1, sizeof (struct tag));
	c = fgetc (fp);
	if (c == '!')	{
		/* this is an HTML comment - whether we see the '--' or not */
		tag->name = strdup ("!--");

		/* eat the two '-'s */
		c = fgetc (fp);
		if (c != '-')	{	/* this covers !DOCTYPE tags */
			while (c != '>')
				c = fgetc (fp);
			return tag;
		}
		c = fgetc (fp);
		for ( ; ; )	{
			c = fgetc (fp);
			while (!feof (fp) && c != '-')
				c = fgetc (fp);
			if (feof (fp))
				break;
			c = fgetc (fp);
			if (c != '-')
				continue;
			c = fgetc (fp);
			while (!feof (fp) && c != '>')
				c = fgetc (fp);
			break;
		}
		return tag;
	}
	if (c == '/')	{
		tag->endtag = 1;
		c = fgetc (fp);
	}

	while (!isspace (c) && c != '>')	{
		buf[theindex++] = (char) c;
		c = fgetc (fp);
	}
	buf[theindex] = 0;
	(void) strlwr (buf);
	tag->name = strdup (buf);
	while (c != '>')	{
		theindex = 0;
		opt = (struct options *) callocw (1, sizeof (struct options));
		opt->next = tag->opts;
		tag->opts = opt;
	
		c = fgetc (fp);
		while (!isspace (c) && c != '>' && c != '=')	{
			buf[theindex++] = (char) c;
			c = fgetc (fp);
		}
		buf[theindex] = 0;
		(void) strlwr (buf);
		opt->name = strdup(buf);
		if (c != '=')
			continue;

		theindex = 0;
		c = fgetc (fp);
		lookfor = ' ';

		if (c == '"')	{
			endquote = 1;
			lookfor = c;
			c = fgetc (fp);
		} else
			endquote = 0;
		while (c != lookfor)	{
			if (!endquote && ((c == '>') || isspace (c)))
				break;
			buf[theindex++] = (char) c;
			c = fgetc (fp);
		}
		buf[theindex] = 0;
		rip (buf);		/* just in case */
		opt->value = strdup (buf);
	}
	return tag;
}



void
delete_tag (struct tag *tag)
{
struct options *opt, *last;

	if (tag == NULLTAG)
		return;

	free (tag->name);
	for (opt = tag->opts; opt != NULLOPTIONS; opt = last)	{
		free (opt->name);
		free (opt->value);
		last = opt->next;
		free (opt);
	}
}



char *
find_option (struct tag *tag, const char *str, int *found)
{
struct options *opt;

	if (found)
		*found = 0;
	for (opt = tag->opts; opt != NULLOPTIONS; opt = opt->next)	{
		if (!strcasecmp (str, opt->name))	{
			if (found)
				*found = 1;
			return (opt->value) ? strdup (opt->value) : NULLCHAR;
		}
	}
	return NULLCHAR;	
}

#ifdef NO_SETENV
char *
setenv (char const *label, char const *value, int unused OPTIONAL)
{
char *temp;

	temp = (char *) mallocw (strlen (label) + strlen (value) + 2);
	sprintf (temp, "%s=%s", label, value);
	(void) putenv (temp);
	free (temp);
	return NULLCHAR;
}
#endif


#endif

