1stMUD/corefiles/
1stMUD/gods/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/* ROM 2.4 Integrated Web Server - Version 1.0
 *
 * This is my first major snippet... Please be kind. ;-)
 * Copyright 1998 -- Defiant -- Rob Siemborski -- mud@towers.crusoe.net
 *
 * Many thanks to Russ and the rest of the developers of ROM for creating
 * such an excellent codebase to program on.
 *
 * If you use this code on your mud, I simply ask that you place my name
 * someplace in the credits.  You can put it where you feel it is
 * appropriate.
 *
 * I offer no guarantee that this will work on any mud except my own, and
 * if you can't get it to work, please don't bother me.  I wrote and tested
 * this only on a Linux 2.0.30 system.  Comments about bugs, are, however,
 * appreciated.
 *
 * Now... On to the installation!
 */

/*
 * Insanity v0.9a pre-release Modifications
 * By Chris Fewtrell (Trax) <C.J.Fewtrell@bcs.org.uk>
 *
 * - Added functionailiy for Secure Web server pages, using standard HTTP
 *   Basic authentication, comparing with pass list generated with command
 *   from within the MUD itself. 
 * - Started work on web interface to help files, allowing them to be browsed
 *   from a web browser rather than being in MUD to read them.
 * - Seperated out the HTTP codes and content type to seperate functions
 *   (intending to allow more than HTML to be served via this)
 * - Adjusted the descriptor handling to prevent anyone from prematurely
 *   stopping a transfer causing a fd exception and the system to exit()
 * - Created a sorta "virtual" web directory for the webserver files to be
 *   actually served. This contains the usual images dir if any images are
 *   needed to be served from a central repository rather than generated.
 *   Be warned though! It WON'T follow any symlinks, I'll add that later
 *   with the stat function.. (maybe :) 
 * - Including a MUD-Net web module to add the functionaility of the MUD-Net webserver
 *   code directly into the mud itself for use here, preventing the need for
 *   the seperate server and client processes (I know it sorta depends on the
 *   mud be FE never stays down for long..)
 *
 * Future Possbile additions:
 * - Access to general boards though web interface, prolly prevent posting but
 *   being able to browse and read notes to 'all' would be allowed
 */

/*
 * Additional Modifications based upon with with Insanity Codebase
 * By Chris Fewtrell (Trax) <C.J.Fewtrell@bcs.org.uk>
 *
 * - Web server root directory created, all URLs that use internal code
 *   generated HTML intercept first, then path is handed off to webroot
 *   handler allowing a directory tree to be built up there. Should allow
 *   easier management of the file behind the internal web server.
 */

#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#if !defined(WIN32)
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#endif
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "merc.h"
#include "webserver.h"
#include "globals.h"
#include "interp.h"
#include "tables.h"
#include "olc.h"
#include "lookup.h"
#include "recycle.h"
#include "magic.h"
#include "gsn.h"
#include "telnet.h"

void update_webpasses(CHAR_DATA * ch, bool pDelete)
{
	WPWD_DATA *c_next;
	WPWD_DATA *curr;

	if (IS_NPC(ch))
		return;

	for (curr = wpwd_first; curr != NULL; curr = c_next)
	{
		c_next = curr->next;

		if (!str_cmp(ch->name, curr->name))
		{
			UNLINK(curr, wpwd_first, wpwd_last, next, prev);
			free_pwd(curr);
			save_webpasses();
		}
	}
	if (pDelete || IS_NULLSTR(ch->pcdata->webpass))
		return;

	curr = new_pwd();
	replace_string(curr->name, ch->name);
	replace_string(curr->passw, ch->pcdata->webpass);
	LINK(curr, wpwd_first, wpwd_last, next, prev);
	save_webpasses();
	return;
}

CH_CMD(do_webpass)
{
	char arg1[MIL];
	char arg2[MIL];
	char *pArg;
	char *pwdnew;
	char *p;
	char cEnd;

	if (!ch || IS_NPC(ch))
		return;

	if (!IS_IMMORTAL(ch))
	{
		chprintln(ch, "This feature is only available to immortals, sorry.");
		return;
	}

	pArg = arg1;
	while (isspace(*argument))
		argument++;

	cEnd = ' ';
	if (*argument == '\'' || *argument == '\"')
		cEnd = *argument++;

	while (*argument != '\0')
	{
		if (*argument == cEnd)
		{
			argument++;
			break;
		}
		*pArg++ = *argument++;
	}
	*pArg = '\0';

	pArg = arg2;
	while (isspace(*argument))
		argument++;

	cEnd = ' ';
	if (*argument == '\'' || *argument == '\"')
		cEnd = *argument++;

	while (*argument != '\0')
	{
		if (*argument == cEnd)
		{
			argument++;
			break;
		}
		*pArg++ = *argument++;
	}
	*pArg = '\0';

	if (IS_NULLSTR(arg1) || IS_NULLSTR(arg2))
	{
		chprintln(ch, "Syntax: webpass <old> <new>.");
		return;
	}

	write_to_descriptor(ch->desc, echo_off_str, 0);

	if (str_cmp(crypt(arg1, ch->pcdata->webpass), ch->pcdata->webpass))
	{
		WAIT_STATE(ch, 40);
		chprintln(ch, "Wrong password.  Wait 10 seconds.");
		write_to_descriptor(ch->desc, echo_on_str, 0);
		return;
	}

	if (strlen(arg2) < 5)
	{
		chprintln(ch, "New password must be at least five characters long.");
		write_to_descriptor(ch->desc, echo_on_str, 0);
		return;
	}

	pwdnew = crypt(arg2, ch->name);
	for (p = pwdnew; *p != '\0'; p++)
	{
		if (*p == '~')
		{
			chprintln(ch, "New password not acceptable, try again.");
			write_to_descriptor(ch->desc, echo_on_str, 0);
			return;
		}
	}

	replace_string(ch->pcdata->webpass, pwdnew);
	save_char_obj(ch);
	update_webpasses(ch, FALSE);
	chprintln(ch, "Ok.");
	write_to_descriptor(ch->desc, echo_on_str, 0);
	return;
}

bool check_web_pass(const char *username, const char *password)
{
	WPWD_DATA *current;

    for (current = wpwd_first; current; current = current->next)
		if (!strcasecmp(current->name, username))
			if (!strcasecmp(current->passw, crypt(password, username)))
				return TRUE;

	return FALSE;
}

/* Thanks to John Ludeman for this code...
 * Define translation matrix for Base64 decode.
 * it's fast and const should make it shared text page.
 */
const int pr2six[256] = {
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	62, 64, 64, 64, 63,
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2,
	3, 4, 5, 6, 7, 8, 9,
	10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64,
	64, 64, 64, 26, 27,
	28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
	47, 48, 49, 50, 51,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
	64, 64, 64, 64, 64,
	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};

bool WebUP = FALSE;

/*
 * Decode a base64 encoded string
 */
void Base64Decode(char *bufcoded, unsigned char *bufplain, int outbufsize)
{
	int nbytesdecoded;
	int nprbytes;
	char *bufin = bufcoded;
	unsigned char *bufout = bufplain;

	/* Strip leading whitespace */
	while (*bufcoded == ' ' || *bufcoded == '\t')
		++bufcoded;

	/*
	 * Figure out how many characters are in the input buffer.
	 * If this would decode into more bytes than would fit into
	 * the output buffer, adjust the number of input bytes downwards.
	 */
	bufin = bufcoded;
	while (pr2six[(int) *(bufin++)] <= 63)
		;
	nprbytes = bufin - bufcoded - 1;
	nbytesdecoded = ((nprbytes + 3) / 4) * 3;
	if (nbytesdecoded > outbufsize)
		nprbytes = (outbufsize * 4) / 3;

	bufin = bufcoded;
	while (nprbytes > 0)
	{
		*(bufout++) =
			(unsigned char) (pr2six[(int) *bufin] << 2 | pr2six[(int) bufin[1]]
							 >> 4);
		*(bufout++) =
			(unsigned char) (pr2six[(int) bufin[1]] << 4 |
							 pr2six[(int) bufin[2]] >> 2);
		*(bufout++) =
			(unsigned char) (pr2six[(int) bufin[2]] << 6 |
							 pr2six[(int) bufin[3]]);
		bufin += 4;
		nprbytes -= 4;
	}

	if (nprbytes & 03)
	{
		if (pr2six[(int) bufin[-2]] > 63)
			nbytesdecoded -= 2;
		else
			nbytesdecoded -= 1;
	}
	bufplain[nbytesdecoded] = '\0';
}

/* The mark of the end of a HTTP/1.x request */
const char ENDREQUEST[5] = { 13, 10, 13, 10, 0 };	/* (CRLFCRLF) */

/* Locals */
WEB_DESCRIPTOR *first_webdesc;
WEB_DESCRIPTOR *last_webdesc;
int web_socket;

bool init_web_server()
{
	struct sockaddr_in my_addr;
	int x = 1;

	first_webdesc = NULL;
	last_webdesc = NULL;

	logf("Attaching Internal Web Server to Port %d", WEBSERVERPORT);

	if ((web_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		log_string("----> Web Server: Error getting socket");
		perror("web-socket");
		return FALSE;
	}

	if (setsockopt(web_socket, SOL_SOCKET, SO_REUSEADDR, (void *) &x, sizeof(x))
		< 0)
	{
		perror("init_web: SO_REUSEADDR");
		close(web_socket);
		web_socket = -1;
		return FALSE;
	}

#if defined(SO_LINGER) && !defined(SYSV)
	{
		struct linger ld;

		ld.l_onoff = 1;
		ld.l_linger = 1000;

		if (setsockopt
			(web_socket, SOL_SOCKET, SO_LINGER, (void *) &ld, sizeof(ld)) < 0)
		{
			perror("Init_web: SO_LINGER");
			close(web_socket);
			web_socket = -1;
			return FALSE;
		}
	}
#endif

	memset(&my_addr, '\0', sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(WEBSERVERPORT);

	if ((bind(web_socket, (struct sockaddr *) &my_addr, sizeof(my_addr))) == -1)
	{
		log_string("----> Web Server: Error binding socket");
		perror("web-bind");
		close(web_socket);
		web_socket = -1;
		return FALSE;
	}

	/* Only listen for 5 connects at once, do we really need more? */
	/* We might now since I've attached this to the live web page - Samson */
	if (listen(web_socket, 20) < 0)
	{
		perror("web-listen");
		close(web_socket);
		web_socket = -1;
		return FALSE;
	}
	return TRUE;
}

struct timeval ZERO_TIME = { 0, 0 };

void update_web_server(void)
{
	int max_fd;
	WEB_DESCRIPTOR *current, *next;
	fd_set readfds;

	FD_ZERO(&readfds);
	FD_SET(web_socket, &readfds);

	/* it *will* be atleast web_socket */
	max_fd = web_socket;

	/* add in all the current web descriptors */
	for (current = first_webdesc; current; current = current->next)
	{
		FD_SET(current->fd, &readfds);
		if (max_fd < current->fd)
			max_fd = current->fd;
	}

	/* Wait for ONE descriptor to have activity */
	select(max_fd + 1, &readfds, NULL, NULL, &ZERO_TIME);

	if (FD_ISSET(web_socket, &readfds))
	{
		/* NEW CONNECTION -- INIT & ADD TO LIST */

		alloc_mem(current, WEB_DESCRIPTOR, 1);
		current->sin_size = sizeof(struct sockaddr_in);
		current->request[0] = '\0';

		if ((current->fd =
			 accept(web_socket, (struct sockaddr *) &(current->their_addr),
					&(current->sin_size))) == -1)
		{
			log_string("----> Web Server: Error accepting connection");
			perror("web-accept");
			free_mem(current);
			FD_CLR(web_socket, &readfds);
			return;
		}

		LINK(current, first_webdesc, last_webdesc, next, prev);
		/* END ADDING NEW DESC */
	}

	/* DATA IN! */
	for (current = first_webdesc; current; current = current->next)
	{
		if (FD_ISSET(current->fd, &readfds))	/* We Got Data! */
		{
			char buf[MAXDATA];
			int numbytes;

			if ((numbytes = read(current->fd, buf, sizeof(buf))) == -1)
			{
				perror("web-read");
				continue;
			}

			buf[numbytes] = '\0';

			strcat(current->request, buf);
		}
	}							/* DONE WITH DATA IN */

	/* DATA OUT */
	/* Hmm we want to delay this if possible, to prevent it prematurely */
	for (current = first_webdesc; current; current = next)
	{
		next = current->next;

		if (strstr(current->request, "HTTP/1.")	/* 1.x request (vernum on FIRST LINE) */
			&& strstr(current->request, ENDREQUEST))
			handle_web_request(current);
		else if (!strstr(current->request, "HTTP/1.")
				 && strchr(current->request, '\n'))	/* HTTP/0.9 (no ver number) */
			handle_web_request(current);
		else
		{
			continue;			/* Don't have full request yet! */
		}

		close(current->fd);
		UNLINK(current, first_webdesc, last_webdesc, next, prev);
		free_mem(current);
	}
	/* END DATA-OUT */
}

/* Generic Utility Function */
int send_buf(int fd, const char *fmt, ...)
{
	char buf[2 * MSL];
	va_list args;
	int len;

	va_start(args, fmt);

	len = vsnprintf(buf, 2 * MSL, fmt, args);
	va_end(args);

	if (len > 0)
		return send(fd, buf, strlen(buf), 0);
	else
		return len;
}

void send_401UNAUTHORISED(WEB_DESCRIPTOR * wdesc, char *realm)
{
	send_buf(wdesc->fd, "HTTP/1.1 401 Unauthorised\n");
	send_buf(wdesc->fd, "WWW-Authenticate: Basic realm=\"%s\"\n", realm);
}

bool lastcolor = FALSE;

#define SET_WWW_FONT(buf, color) \
do \
{ \
    strcpy(buf, "<FONT color=\"" color "\">"); \
	lastcolor = TRUE; \
} \
while (0);

int html_colour(char type, char *string)
{
	char code[MIL];
	char out[MSL];
	char *p = '\0';

	if (lastcolor == TRUE)
		strncpy(out, "</FONT>", MSL);
	else
		out[0] = '\0';

	switch (type)
	{
	case '\0':
		break;
	case ' ':
		strncpy(code, "&nbsp;", MIL);
		break;
	default:
	case 'x':
		SET_WWW_FONT(code, "silver");
		break;
	case 'b':
		SET_WWW_FONT(code, "navy");
		break;
	case 'c':
		SET_WWW_FONT(code, "teal");
		break;
	case 'g':
		SET_WWW_FONT(code, "green");
		break;
	case 'm':
		SET_WWW_FONT(code, "purple");
		break;
	case 'r':
		SET_WWW_FONT(code, "maroon");
		break;
	case 'w':
		SET_WWW_FONT(code, "silver");
		break;
	case 'y':
		SET_WWW_FONT(code, "olive");
		break;
	case 'B':
		SET_WWW_FONT(code, "blue");
		break;
	case 'C':
		SET_WWW_FONT(code, "cyan");
		break;
	case 'G':
		SET_WWW_FONT(code, "lime");
		break;
	case 'M':
		SET_WWW_FONT(code, "magenta");
		break;
	case 'R':
		SET_WWW_FONT(code, "red");
		break;
	case 'W':
		SET_WWW_FONT(code, "white");
		break;
	case 'Y':
		SET_WWW_FONT(code, "yellow");
		break;
	case 'd':
		SET_WWW_FONT(code, "black");
		break;
	case 'D':
		SET_WWW_FONT(code, "gray");
		break;
	case '-':
		strncpy(code, "~", MIL);
		break;
	case '`':
		switch (number_range(1, 14))
		{
		case 1:
			SET_WWW_FONT(code, "navy");
			break;
		case 2:
			SET_WWW_FONT(code, "teal");
			break;
		case 3:
			SET_WWW_FONT(code, "green");
			break;
		case 4:
			SET_WWW_FONT(code, "purple");
			break;
		case 5:
			SET_WWW_FONT(code, "maroon");
			break;
		default:
		case 6:
			SET_WWW_FONT(code, "silver");
			break;
		case 7:
			SET_WWW_FONT(code, "olive");
			break;
		case 8:
			SET_WWW_FONT(code, "blue");
			break;
		case 9:
			SET_WWW_FONT(code, "cyan");
			break;
		case 10:
			SET_WWW_FONT(code, "lime");
			break;
		case 11:
			SET_WWW_FONT(code, "magenta");
			break;
		case 12:
			SET_WWW_FONT(code, "red");
			break;
		case 13:
			SET_WWW_FONT(code, "white");
			break;
		case 14:
			SET_WWW_FONT(code, "yellow");
			break;
		}
		break;
	case ANSI_KEY:
		strncpy(code, "{", MIL);
		break;
	}

	strcat(out, code);

	p = out;
	while (*p != '\0')
	{
		*string = *p++;
		*++string = '\0';
	}

	return (strlen(out));
}

void html_colourconv(char *buffer, const char *txt)
{
	const char *point;
	int skip = 0;
	const char *end = "</FONT>";

	lastcolor = FALSE;

	for (point = txt; *point; point++)
	{
		if (*point == ANSI_KEY)
		{
			point++;
			if (*point == '\0')
				point--;
			else
				skip = html_colour(*point, buffer);
			while (skip-- > 0)
				++buffer;
			continue;
		}
		else if (*point == ANSI_CUSTOM)
		{
			point++;
			while (*point != ANSI_END)
				point++;
			continue;
		}
		if (*point == '<')
		{
			*buffer = '&';
			*++buffer = 'l';
			*++buffer = 't';
			*++buffer = ';';
			*++buffer = '\0';
			continue;
		}
		if (*point == '>')
		{
			*buffer = '&';
			*++buffer = 'g';
			*++buffer = 't';
			*++buffer = ';';
			*++buffer = '\0';
			continue;
		}
		if (*point == '"')
		{
			*buffer = '&';
			*++buffer = 'q';
			*++buffer = 'u';
			*++buffer = 'o';
			*++buffer = 't';
			*++buffer = ';';
			*++buffer = '\0';
			continue;
		}
		if (*point == '&')
		{
			*buffer = '&';
			*++buffer = 'a';
			*++buffer = 'm';
			*++buffer = 'p';
			*++buffer = '\0';
			continue;
		}
		if (*point == '\n')
		{
			*buffer = '<';
			*++buffer = 'b';
			*++buffer = 'r';
			*++buffer = '>';
			*++buffer = '\0';
		}
		*buffer = *point;
		*++buffer = '\0';
	}

	if (lastcolor == TRUE)
	{
		for (point = end; *point; point++)
		{
			*buffer = *point;
			*++buffer = '\0';
		}
	}

	*buffer = '\0';
	return;
}

const char *DEFAULT_URL_PORT(void)
{
	static char result[100];
	int len;

	sprintf(result, "%s", DEFAULT_URL);
	len = strlen(result);
	if (result[len - 1] == '/')
		sprintf(result + (len - 1), ":%d/", WEBSERVERPORT);	// get rid of appending backslash
	else
		sprintf(result + len, ":%d/", WEBSERVERPORT);
	return result;
}

void under_line(char *under_lined, const char *spaced_out)
{
	char *point;

	strcpy(under_lined, spaced_out);

	for (point = under_lined; *point; point++)
	{
		if (*point == ' ')
		{
			*point = '_';
		}
		else
		{
			*point = LOWER(*point);
		}
	}
	return;
}

int min_class_level(int sn)
{
	int min_so_far = MAX_LEVEL;
	int iClass;

	for (iClass = 0; iClass < maxClass; iClass++)
	{
		if (skill_table[sn].skill_level[iClass] < min_so_far)
			min_so_far = skill_table[sn].skill_level[iClass];
	}
	return min_so_far;
}

void print_header(WEB_DESCRIPTOR * wdesc, const char *title)
{
	send_buf(wdesc->fd, DOCTYPE);
	send_buf(wdesc->fd, "<HTML>\n");
	send_buf(wdesc->fd, "<HEAD>\n");
	send_buf(wdesc->fd, "<TITLE>%s @ %s</TITLE>\n", title, MUD_NAME);
	send_buf(wdesc->fd, "</HEAD><BODY>\n");
	send_buf(wdesc->fd, "<H2>%s @ %s</H2>\n", title, MUD_NAME);
}

void print_footer(WEB_DESCRIPTOR * wdesc)
{
	send_buf(wdesc->fd, "<BR><ADDRESS>\n");
	send_buf(wdesc->fd,
			 "This page is automatically generated by " MUD_NAME " Mud.<BR>\n");
	send_buf(wdesc->fd, "</ADDRESS>\n");
	send_buf(wdesc->fd, "</BODY>\n");
	send_buf(wdesc->fd, "</HTML>\n");
}

void print_file(WEB_DESCRIPTOR * wdesc, char *filename)
{
	FILE *fp;
	char buf[MSL * 4];
	int c;
	int num = 0;

	if ((fp = file_open(filename, "r")) != NULL)
	{
		while (!feof(fp))
		{
			while ((buf[num] = fgetc(fp)) != EOF && buf[num] != '\n'
				   && buf[num] != '\r' && num < (MSL * 4 - 2))
				num++;
			c = fgetc(fp);
			if ((c != '\n' && c != '\r') || c == buf[num])
				ungetc(c, fp);
			buf[num++] = '\n';
			buf[num] = '\0';
			send_buf(wdesc->fd, buf);
			num = 0;
		}

		file_close(fp);
	}
	else
	{
		logf("Failed to open file %s.", filename);
		file_close(fp);
	}
}

char *format_obj_to_html(OBJ_DATA * obj)
{
	AFFECT_DATA *paf;
	static char output[MSL * 5];
	char buf[MSL * 5];

	output[0] = '\0';

	if (IS_NULLSTR(obj->description))
		return output;

	if (IS_OBJ_STAT(obj, ITEM_INVIS))
		strcat(output, "{W(Invis) ");
	if (IS_OBJ_STAT(obj, ITEM_DARK))
		strcat(output, "{D(Dark) ");
	if (IS_OBJ_STAT(obj, ITEM_EVIL))
		strcat(output, "{R(Red Aura) ");
	if (IS_OBJ_STAT(obj, ITEM_BLESS))
		strcat(output, "{B(Blue Aura) ");
	if (IS_OBJ_STAT(obj, ITEM_MAGIC))
		strcat(output, "{G(Magical) ");
	if (IS_OBJ_STAT(obj, ITEM_GLOW))
		strcat(output, "{Y(Glowing) ");
	if (IS_OBJ_STAT(obj, ITEM_HUM))
		strcat(output, "{C(Humming) ");

	if (obj->item_type == ITEM_WEAPON)
	{
		for (paf = obj->first_affect; paf; paf = paf->next)
		{
			if (IS_SET(paf->bitvector, WEAPON_FLAMING))
				strcat(output, "{Y(Flaming){x ");
			if (IS_SET(paf->bitvector, WEAPON_FROST))
				strcat(output, "{C(Frost){x ");
			if (IS_SET(paf->bitvector, WEAPON_VAMPIRIC))
				strcat(output, "{R(Vampiric){x ");
			if (IS_SET(paf->bitvector, WEAPON_SHOCKING))
				strcat(output, "{M(Shocking){x ");
			if (IS_SET(paf->bitvector, WEAPON_POISON))
				strcat(output, "{g(Poison){x ");
			if (IS_SET(paf->bitvector, WEAPON_SHARP))
				strcat(output, "{W(Sharp){x ");
			if (IS_SET(paf->bitvector, WEAPON_VORPAL))
				strcat(output, "{r(Vorpal){x ");
		}
	}

	if (IS_OBJ_STAT(obj, ITEM_AUCTIONED))
		strcat(output, "{Y(Auctioned){x ");

	if (obj->condition <= 9 && obj->condition >= 0)
		strcat(output, "{b(Ruined) ");
	else if (obj->condition >= 10 && obj->condition <= 24)
		strcat(output, "{b(Broken) ");

	strcat(output, "{x");

	html_colourconv(buf, output);
	sprintf(output, buf);

	if (obj->description != NULL)
	{
		char temp[MSL];

		html_colourconv(buf, obj->description);
		sprintf(temp, "<A href=\"%sobjs/%ld\">%s</A><br>",
				DEFAULT_URL_PORT(), obj->pIndexData->vnum, buf);
		strcat(output, temp);
	}

	return output;
}

char *show_list_to_html(OBJ_DATA * list)
{
	static char output[MSL * 6];
	const char **prgpstrShow;
	int *prgnShow;
	char *pstrShow;
	OBJ_DATA *obj;
	int nShow;
	int iShow;
	int count;
	char temp[MSL];
	bool found;

	count = 0;
	for (obj = list; obj != NULL; obj = obj->next_content)
		count++;

	alloc_mem(prgpstrShow, const char *, count);
	alloc_mem(prgnShow, int, count);

	nShow = 0;

	output[0] = '\0';

	for (obj = list; obj != NULL; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE)
		{
			pstrShow = format_obj_to_html(obj);
			found = FALSE;

			for (iShow = nShow - 1; iShow >= 0; iShow--)
			{
				if (!str_cmp(prgpstrShow[iShow], pstrShow))
				{
					prgnShow[iShow]++;
					found = TRUE;
					break;
				}
			}
			if (!found)
			{
				prgpstrShow[nShow] = str_dup(pstrShow);
				prgnShow[nShow] = 1;
				nShow++;
			}
		}
	}

	for (iShow = 0; iShow < nShow; iShow++)
	{
		if (IS_NULLSTR(prgpstrShow[iShow]))
		{
			free_string(prgpstrShow[iShow]);
			continue;
		}

		if (prgnShow[iShow] != 1)
		{
			sprintf(temp, "(%2d) ", prgnShow[iShow]);
			strcat(output, temp);
		}
		else
			strcat(output, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");

		sprintf(temp, "%s<br>", prgpstrShow[iShow]);
		strcat(output, temp);

		free_string(prgpstrShow[iShow]);

		if (strlen(output) > 5500)
		{
			strcat(output, "     (More stuff not shown)<br>");
			break;
		}
	}

	free_mem(prgpstrShow);
	free_mem(prgnShow);
	return output;
}

char *show_char_to_html_0(CHAR_DATA * victim)
{
	static char buf[MSL * 6];
	char cbuf[MSL * 6];
	char temp[MSL];

	buf[0] = '\0';

	if (IS_AFFECTED(victim, AFF_INVISIBLE))
		strcat(buf, "({wInvis{x) ");
	if (victim->invis_level >= LEVEL_IMMORTAL)
		strcat(buf, "({WWizi{x) ");
	if (IS_AFFECTED(victim, AFF_HIDE))
		strcat(buf, "({DHide{x) ");
	if (IS_AFFECTED(victim, AFF_CHARM))
		strcat(buf, "({MCharmed{x) ");
	if (IS_AFFECTED(victim, AFF_PASS_DOOR))
		strcat(buf, "({cTranslucent{x) ");
	if (IS_AFFECTED(victim, AFF_FAERIE_FIRE))
		strcat(buf, "({mPink Aura{x) ");
	if (IS_EVIL(victim))
		strcat(buf, "({RRed Aura{x) ");
	if (IS_GOOD(victim))
		strcat(buf, "({YGolden Aura{x) ");
	if (IS_AFFECTED(victim, AFF_SANCTUARY))
		strcat(buf, "({WWhite Aura{x) ");
	if (!IS_NPC(victim) && !victim->desc)
		strcat(buf, "({CLinkdead{x) ");

	if (!IS_NPC(victim))
	{
		if (IS_SET(victim->comm, COMM_AFK))
			strcat(buf, "{Y*{RAFK{Y*{x ");
		if (IS_SET(victim->act, PLR_WAR))
			strcat(buf, "{Y({RWAR{Y){x ");
		if (!IS_NPC(victim) && IS_SET(victim->act, PLR_KILLER))
			strcat(buf, "({rKILLER{x) ");
		if (!IS_NPC(victim) && IS_SET(victim->act, PLR_THIEF))
			strcat(buf, "({rTHIEF{x) ");
		if (IS_SET(victim->comm, COMM_QUIET))
			strcat(buf, "{R[{WQUIET{R]{x ");
		if (victim->desc && victim->desc->editor != 0)
			strcat(buf, "{M[{mOLC{M]{x ");
		if (victim->pcdata->in_progress != NULL)
			strcat(buf, "{g[{wNote{g]{x ");
		if (IS_QUESTOR(victim))
			strcat(buf, "{W[{CQ{W]{x ");
	}

	if (victim->level > 0)
	{
		long vict_condition;

		if (victim->max_hit > 0)
			vict_condition = victim->hit * 100 / victim->max_hit;
		else
			vict_condition = -1;

		if (vict_condition < 0)
			strcat(buf, "{R(DEAD){x ");
		else if (vict_condition < 33)
			strcat(buf, "{R(Wounded){x ");
	}

	if (victim->position ==
		(IS_NPC(victim) ? victim->start_pos : POS_STANDING)
		&& !IS_NULLSTR(victim->long_descr))
	{
		html_colourconv(cbuf, buf);
		strcpy(buf, cbuf);
		if (IS_NPC(victim))
		{
			html_colourconv(cbuf, victim->long_descr);
			sprintf(temp, "<A href=\"%schars/%ld\">%s</A>", DEFAULT_URL_PORT(),
					victim->pIndexData->vnum, cbuf);
			strcat(buf, temp);
		}
		return (buf);
	}
	if (IS_NPC(victim))
	{
		html_colourconv(cbuf, victim->short_descr);
		sprintf(temp, "<A href=\"%schars/%ld\">%s</A>", DEFAULT_URL_PORT(),
				victim->pIndexData->vnum, cbuf);
		strcat(buf, temp);
	}
	else
	{
		html_colourconv(cbuf, buf);
		strcat(buf, cbuf);
		strcat(buf, victim->name);
	}

	switch (victim->position)
	{
	case POS_DEAD:
		strcat(buf, " is DEAD!!");
		break;
	case POS_MORTAL:
		strcat(buf, " is mortally wounded.");
		break;
	case POS_INCAP:
		strcat(buf, " is incapacitated.");
		break;
	case POS_STUNNED:
		strcat(buf, " is lying here stunned.");
		break;
	case POS_SLEEPING:
		if (victim->on != NULL)
		{
			if (IS_SET(victim->on->value[2], SLEEP_AT))
			{
				sprintf(temp, " is sleeping at %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else if (IS_SET(victim->on->value[2], SLEEP_ON))
			{
				sprintf(temp, " is sleeping on %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else
			{
				sprintf(temp, " is sleeping in %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
		}
		else
			strcat(buf, " is sleeping here.");
		break;
	case POS_RESTING:
		if (victim->on != NULL)
		{
			if (IS_SET(victim->on->value[2], REST_AT))
			{
				sprintf(temp, " is resting at %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else if (IS_SET(victim->on->value[2], REST_ON))
			{
				sprintf(temp, " is resting on %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else
			{
				sprintf(temp, " is resting in %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
		}
		else
			strcat(buf, " is resting here.");
		break;
	case POS_SITTING:
		if (victim->on != NULL)
		{
			if (IS_SET(victim->on->value[2], SIT_AT))
			{
				sprintf(temp, " is sitting at %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else if (IS_SET(victim->on->value[2], SIT_ON))
			{
				sprintf(temp, " is sitting on %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else
			{
				sprintf(temp, " is sitting in %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
		}
		else
			strcat(buf, " is sitting here.");
		break;
	case POS_STANDING:
		if (victim->on != NULL)
		{
			if (IS_SET(victim->on->value[2], STAND_AT))
			{
				sprintf(temp, " is standing at %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else if (IS_SET(victim->on->value[2], STAND_ON))
			{
				sprintf(temp, " is standing on %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
			else
			{
				sprintf(temp, " is standing in %s.", victim->on->short_descr);
				strcat(buf, temp);
			}
		}
		strcat(buf, " is here.");
		break;
	case POS_FIGHTING:
		strcat(buf, " is here, fighting ");
		if (victim->fighting == NULL)
			strcat(buf, "thin air??");
		else if (victim->in_room == victim->fighting->in_room)
		{
			strcat(buf,
				   IS_NPC(victim) ? victim->fighting->
				   short_descr : victim->fighting->name);
			strcat(buf, ".");
		}
		else
			strcat(buf, "someone who left??");
		break;
	}
	return (buf);
}

char *show_char_to_html(CHAR_DATA * list)
{
	CHAR_DATA *rch;
	static char output[MSL * 10];

	output[0] = '\0';
	for (rch = list; rch != NULL; rch = rch->next_in_room)
	{
		if (rch->invis_level >= LEVEL_IMMORTAL)
			continue;

		strcat(output, show_char_to_html_0(rch));
		strcat(output, "<br>");
	}
	return (output);
}

void HandleMemoryRequest(WEB_DESCRIPTOR * wdesc)
{
	int sn, count_spell = 0, count_skill = 0;

	for (sn = 0; sn < maxSkill; sn++)
	{
		if (skill_table[sn].name == NULL)
			break;

		if (skill_table[sn].spell_fun != spell_null)
			count_spell += 1;
		else
			count_skill += 1;
	}
	print_header(wdesc, "Technical Info");
	send_buf(wdesc->fd, "<TABLE>\n");
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Affects</TD>  <TD>%5d</TD></TR>\n", top_affect);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Areas</TD> <TD>%5d</TD></TR>\n", top_area);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>ExDes</TD> <TD>%5d</TD></TR>\n", top_ed);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Exits</TD> <TD>%5d</TD></TR>\n", top_exit);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Helps</TD> <TD>%5d</TD></TR>\n", top_help);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Socials</TD> <TD>%5d</TD></TR>\n", maxSocial);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Spells</TD> <TD>%5d</TD></TR>\n", count_spell);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Skills</TD> <TD>%5d</TD></TR>\n", count_skill);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd,
			 "<TD>Mobs</TD> <TD>%5d (%5d in use)</TD></TR>\n",
			 top_mob_index, mobile_count);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Objs</TD> <TD>%5d</TD></TR>\n", top_obj_index);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Resets</TD> <TD>%5d</TD></TR>\n", top_reset);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Rooms</TD> <TD>%5d</TD></TR>\n", top_room);
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Shops</TD> <TD>%5d</TD></TR>\n", top_shop);
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);

}

void HandleRulesRequest(WEB_DESCRIPTOR * wdesc)
{
	HELP_DATA *pHelp;

	print_header(wdesc, "Rules");
	send_buf(wdesc->fd, "<PRE>\n");
	if ((pHelp = help_lookup("RULES")) != NULL)
		send_buf(wdesc->fd, fix_string(smash_colour(pHelp->text)));
	else
		send_buf(wdesc->fd, "There are no rules!! Anarchy!!\n");
	send_buf(wdesc->fd, "</PRE>\n");
	print_footer(wdesc);
}

void HandleAreaRequest(WEB_DESCRIPTOR * wdesc)
{
	AREA_DATA *pArea;
	int count = 0;

	print_header(wdesc, "Areas");
	send_buf(wdesc->fd, "<TABLE>\n");
	send_buf(wdesc->fd, "<TR>\n");
	send_buf(wdesc->fd, "<TD>Low/High<BR>Level</TD>\n");
	send_buf(wdesc->fd, "<TD>Author</TD>\n");
	send_buf(wdesc->fd, "<TD>Area Name</TD></TR>\n");
	for (pArea = area_first;;)
	{
		if (pArea == NULL)
			break;
		if (!IS_SET(pArea->area_flags, AREA_CLOSED | AREA_PLAYER_HOMES))
		{
			send_buf(wdesc->fd, "<TR>\n");
			send_buf(wdesc->fd, "<TD>%s</TD>\n", pArea->credits);
			send_buf(wdesc->fd, "<TD>%s</TD></TR>\n", pArea->name);
			count++;
		}
		pArea = pArea->next;
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	send_buf(wdesc->fd, "<P>Areas Found: %d.</P>\n", count);
	print_footer(wdesc);
}

void HandleSpellsRequest(WEB_DESCRIPTOR * wdesc)
{
	int gn;
	int sn, i;
	int tn, race;
	bool *displayed;
	int past_default = TRUE;
	char buf[MSL];
	char buf2[MSL];
	char buf3[MSL];

	alloc_mem(displayed, bool, maxSkill);
	memset(displayed, FALSE, maxSkill);

	print_header(wdesc, "Spells and Skills");

	for (gn = 0; gn < maxGroup; gn++)
	{
		if (group_table[gn].name == NULL)
			break;

		if (!str_cmp(group_table[gn].name, "rom basics"))
		{
			send_buf(wdesc->fd, "<H3>Basic Skills</H3>\n");
			send_buf(wdesc->fd,
					 "All players receive the <I>basic</I> skills for their class.<BR>\n");
			send_buf(wdesc->fd, "<TABLE><TR>\n");
			send_buf(wdesc->fd, "<TD>Basic Group</TD>\n");
			send_buf(wdesc->fd, "<TD>Skills Included</TD></TR>\n");
		}

		if (!str_cmp(group_table[gn].name, "mage default"))
		{

			past_default = FALSE;
			send_buf(wdesc->fd, "</TABLE>\n");
			send_buf(wdesc->fd, "<H3>Default Skill/Spell Groups</H3>\n");
			send_buf(wdesc->fd,
					 "Players receive the <I>default</I> skills and spell groups if they bypass customization.<BR>\n");
			send_buf(wdesc->fd, "<TABLE><TR>\n");
			send_buf(wdesc->fd, "<TD>Default Group</TD>\n");
			send_buf(wdesc->fd, "<TD>Skills Included</TD>\n");
			send_buf(wdesc->fd, "<TD>Skill/Spell Groups Included</TD></TR>\n");
		}

		if (!str_cmp(group_table[gn].name, "weaponsmaster"))
		{
			past_default = TRUE;
			send_buf(wdesc->fd, "</TABLE>\n");
			send_buf(wdesc->fd, "<H3>Other Skill/Spell Groups</H3>\n");
			send_buf(wdesc->fd,
					 "Players may gain these skills and spells.<BR>\n");
			send_buf(wdesc->fd, "<TABLE><TR>\n");
			send_buf(wdesc->fd, "<TD>Skill/Spell Group</TD>\n");
			send_buf(wdesc->fd, "<TD>Skills/Spells Included</TD></TR>\n");
		}

		under_line(buf3, group_table[gn].name);
		send_buf(wdesc->fd, "<TR><TD><A NAME=\"%s\">%s</A></TD><TD>",
				 buf3, group_table[gn].name);

		buf[0] = '\0';
		for (sn = 0; sn < MAX_IN_GROUP; sn++)
		{
			if (group_table[gn].spells[sn] == NULL)
				break;
			tn = skill_lookup(group_table[gn].spells[sn]);
			if (tn != -1)

			{
				snprintf(buf2, MSL, "%s, ", group_table[gn].spells[sn]);
				strncat(buf, buf2, MSL);

				displayed[tn] = TRUE;
			}
		}
		if (!IS_NULLSTR(buf) && (strlen(buf) > 2))
			buf[(strlen(buf) - 2)] = '\0';

		else
			strncpy(buf, "None", MSL);

		if (!past_default)
		{
			send_buf(wdesc->fd, "%s</TD><TD>\n", buf);

			buf[0] = '\0';
			for (sn = 0; sn < MAX_IN_GROUP; sn++)
			{
				if (group_table[gn].spells[sn] == NULL)
					break;
				tn = skill_lookup(group_table[gn].spells[sn]);
				if (tn == -1)

				{
					under_line(buf3, group_table[gn].spells[sn]);
					snprintf(buf2, MSL, "<A HREF=\"#%s\">%s</A>, ", buf3,
							 group_table[gn].spells[sn]);
					strncat(buf, buf2, MSL);
				}
				else
				{

					displayed[tn] = TRUE;
				}
			}
			if (!IS_NULLSTR(buf) && (strlen(buf) > 2))
				buf[(strlen(buf) - 2)] = '\0';

			else
				strncpy(buf, "None", MSL);
		}
		send_buf(wdesc->fd, "%s</TD></TR>\n", buf);
	}
	send_buf(wdesc->fd, "</TABLE>\n");

	send_buf(wdesc->fd, "<H3>Other Skills and Spells</H3>\n");
	send_buf(wdesc->fd,
			 "The following skills and spells are available to various mortals.<BR>\n");
	send_buf(wdesc->fd, "<TABLE><TR><TD>\n");
	buf[0] = '\0';

	for (sn = 1; sn < maxSkill; sn++)
	{
		if (skill_table[sn].name == NULL)
			break;

		if (!displayed[sn] && (min_class_level(sn) < ANGEL))
		{
			snprintf(buf2, MSL, "%s, ", skill_table[sn].name);
			strncat(buf, buf2, MSL);
			displayed[sn] = TRUE;
		}
	}
	if (!IS_NULLSTR(buf) && (strlen(buf) > 2))
		buf[(strlen(buf) - 2)] = '\0';

	else
		strncpy(buf, "None", MSL);
	send_buf(wdesc->fd, "%s</TD></TR>\n", buf);
	send_buf(wdesc->fd, "</TABLE>\n");

	send_buf(wdesc->fd,
			 "<P>To view skills in class specific tables, visit the <a href=\"%sclass\">Classes</a> section.</P>\n",
			 DEFAULT_URL_PORT());

	send_buf(wdesc->fd, "<H3><A NAME=\"RaceSkills\">Race Skills</A></H3>\n");
	send_buf(wdesc->fd, "The following skills and spells are race specific.\n");

	send_buf(wdesc->fd, "<TABLE><TR>\n");

	send_buf(wdesc->fd, "<TD>Race</TD>\n");
	send_buf(wdesc->fd, "<TD>Skills</TD></TR>\n");

	for (race = 0; race < maxRace; race++)
	{
		if (!race_table[race].pc_race)
			continue;
		send_buf(wdesc->fd, "<TR><TD>%s</TD><TD>\n", race_table[race].name);
		buf[0] = '\0';
		for (sn = 1; sn < maxSkill; sn++)
		{
			if (skill_table[sn].name == NULL)
				break;
			for (i = 0; i < 5; i++)
			{
				if (race_table[race].skills[i] == NULL)
					break;
				if (skill_lookup(race_table[race].skills[i]) == sn)
				{
					snprintf(buf2, MSL, "%s, ", skill_table[sn].name);
					strncat(buf, buf2, MSL);
					displayed[sn] = TRUE;
				}
			}
		}
		if (!IS_NULLSTR(buf) && (strlen(buf) > 2))
			buf[(strlen(buf) - 2)] = '\0';

		else
			strncpy(buf, "None", MSL);
		send_buf(wdesc->fd, "%s</TD></TR>\n", buf);
	}
	send_buf(wdesc->fd, "</TABLE>\n");

	send_buf(wdesc->fd, "<H3>Immortal Skills and Spells</H3>\n");
	send_buf(wdesc->fd,
			 "The following skills and spells are available to various immortals.\n");

	send_buf(wdesc->fd,
			 "This list includes some spells under development.<BR>\n");
	send_buf(wdesc->fd, "<TABLE><TR>\n");

	send_buf(wdesc->fd, "<TD>Spells</TD></TR>\n");
	send_buf(wdesc->fd, "<TR><TD>\n");
	buf[0] = '\0';
	for (sn = 1; sn < maxSkill; sn++)
	{
		if (skill_table[sn].name == NULL)
			break;
		if (!displayed[sn])
		{
			snprintf(buf2, MSL, "%s, ", skill_table[sn].name);
			strncat(buf, buf2, MSL);
			displayed[sn] = TRUE;
		}
	}
	if (!IS_NULLSTR(buf) && (strlen(buf) > 2))
		buf[(strlen(buf) - 2)] = '\0';

	else
		strncpy(buf, "None", MSL);
	send_buf(wdesc->fd, "%s</TD></TR>\n", buf);
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);
	free_mem(displayed);

}

void HandleWhoRequest(WEB_DESCRIPTOR * wdesc)
{
	CHAR_DATA *wch;
	char buf[MSL * 2];

	print_header(wdesc, "Players currently");
	send_buf(wdesc->fd, "<TABLE><TR>\n");
	send_buf(wdesc->fd, "<TD><I>Level</I></TD>\n");
	send_buf(wdesc->fd, "<TD><I>Race</I></TD>\n");
	send_buf(wdesc->fd, "<TD><I>Class</I></TD>\n");
	send_buf(wdesc->fd, "<TD><I>Clan</I></TD>\n");
	send_buf(wdesc->fd, "<TD><I>Name<I></TD></TR>\n");
	for (wch = player_first; wch != NULL; wch = wch->next_player)
	{
		if (wch->invis_level >= LEVEL_IMMORTAL
			|| wch->incog_level >= LEVEL_IMMORTAL)
			continue;

		send_buf(wdesc->fd, "<TR>\n");
		if (IS_NULLSTR(wch->pcdata->who_descr))
		{
			send_buf(wdesc->fd, "<TD>%d</TD>\n", wch->level);
			send_buf(wdesc->fd,
					 "<TD><A href=\"%sraces/%s\">%s</A></TD>\n",
					 DEFAULT_URL_PORT(), race_table[wch->race].name,
					 race_table[wch->race].name);
			send_buf(wdesc->fd, "<TD>%s</TD>\n", class_who(wch));
		}
		else
		{
			html_colourconv(buf, wch->pcdata->who_descr);
			send_buf(wdesc->fd, "<TD COLSPAN=3>%s</TD>\n", buf);
		}

		if (is_clan(wch))
		{
			html_colourconv(buf, clan_table[wch->clan].who_name);
			send_buf(wdesc->fd,
					 "<TD><A href=\"%sclans/%s\">%s</A></TD>\n",
					 DEFAULT_URL_PORT(), clan_table[wch->clan].name, buf);
		}
		else
			send_buf(wdesc->fd, "<TD></TD>\n");

		send_buf(wdesc->fd, "<TD>");
		send_buf(wdesc->fd, wch->name);

		html_colourconv(buf, wch->pcdata->title);
		send_buf(wdesc->fd, buf);
		send_buf(wdesc->fd, "</TD></TR>\n");

	}
	send_buf(wdesc->fd, "</TABLE><BR>\n");
	print_footer(wdesc);
}

const char *stat_type_name[MAX_GAMESTAT] = {
	"PLAYER KILLERS",
	"MOB KILLERS",
	"PK DEATHS",
	"MOB DEATHS",
};

void HandleStatsRequest(WEB_DESCRIPTOR * wdesc)
{
	int pos;

	print_header(wdesc, "Stats");
	send_buf(wdesc->fd, "<OL>\n");
	send_buf(wdesc->fd,
			 "<li><A HREF=\"%sstats/general\">General Stats</A>\n",
			 DEFAULT_URL_PORT());
	for (pos = 0; pos < MAX_GAMESTAT; pos++)
	{
		send_buf(wdesc->fd,
				 "<li><A HREF=\"%sstats/%s\">%s</A>\n", DEFAULT_URL_PORT(),
				 stat_type_name[pos], stat_type_name[pos]);
	}
	send_buf(wdesc->fd, "</OL>\n");
	print_footer(wdesc);
}

int count_statlist args((void));
int compare_stats args((const void *v1, const void *v2));

void HandleStatsTypeRequest(WEB_DESCRIPTOR * wdesc, int type)
{
	char temp[MSL];
	STAT_DATA *curr;
	STAT_DATA **top;
	int count, loop, pos = 0;
	bool found = FALSE;
	extern int compare_type;

	sprintf(temp, "Ranking of %s", stat_type_name[type]);
	print_header(wdesc, temp);
	send_buf(wdesc->fd, "<TABLE>\n");

	alloc_mem(top, STAT_DATA *, count_statlist());

	count = 0;
	compare_type = type;
	loop = 0;
	for (curr = stat_first; curr != NULL; curr = curr->next)
	{
		top[count] = curr;
		count++;
		found = TRUE;
	}

	qsort(top, count, sizeof(*top), compare_stats);

	send_buf(wdesc->fd,
			 "<TR><TD><I>Rank</I></TD><TD><I>Name</I></TD><TD><I>Number</I></TD>"
			 "<TD><I>Rank</I></TD><TD><I>Name</I></TD><TD><I>Number</I></TD></TR>");
	for (loop = 0; loop < count; loop++)
	{
		if (loop >= 50)
			break;

		sprintf(temp, "%s<TD>%2d)</TD><TD>%-20s</TD><TD>%ld</TD>\n",
				pos == 0 ? "<TR>" : "", loop + 1, top[loop]->name,
				top[loop]->gamestat[type]);
		send_buf(wdesc->fd, temp);
		if (++pos % 2 == 0)
		{
			send_buf(wdesc->fd, "</TR>");
			pos = 0;
		}

	}
	if (!found)
		send_buf(wdesc->fd, "<TR><TD COLSPAN=3>No one found yet.</TD></TR>\n");

	send_buf(wdesc->fd, "</TABLE>");
	send_buf(wdesc->fd,
			 "<BR><A href=\"%sstats\">Back to Stats Index</A>\n",
			 DEFAULT_URL_PORT());
	print_footer(wdesc);
	free_mem(top);
}

void HandleCommandsRequest(WEB_DESCRIPTOR * wdesc)
{
	int i;
	int pos = 0;
	HELP_DATA *pHelp;
	int count = 0;

	print_header(wdesc, "Commands");
	send_buf(wdesc->fd, "<TABLE>\n");
	for (i = 0; i < maxCommands; i++)
	{
		if (cmd_table[i].level >= LEVEL_IMMORTAL)
			continue;
		count = 0;
		for (pHelp = help_first; pHelp; pHelp = pHelp->next)
		{
			count++;
			if (is_name(cmd_table[i].name, pHelp->keyword))
				break;
		}
		if (pHelp)
			send_buf(wdesc->fd, "%s<TD><A href=\"%shelps/%d\">%s</TD>\n",
					 (pos == 0) ? "<TR>" : "", DEFAULT_URL_PORT(), count,
					 cmd_table[i].name);
		else
			send_buf(wdesc->fd,
					 "%s<TD>%s</TD>\n",
					 (pos == 0) ? "<TR>" : "", cmd_table[i].name);
		if (++pos % 5 == 0)
		{
			send_buf(wdesc->fd, "</TR>");
			pos = 0;
		}
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);
}

void HandleHelpsRequest(WEB_DESCRIPTOR * wdesc)
{
	HELP_DATA *pHelp;
	int count = 0;
	int pos = 0;

	print_header(wdesc, "Help Files");
	send_buf(wdesc->fd, "<TABLE>\n");
	for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next)
	{
		count++;
		if (pHelp->level <= LEVEL_HERO && pHelp->level >= 0)
		{
			const char *temp;
			char wordkey[MSL];

			temp = pHelp->keyword;
			while (!IS_NULLSTR(temp))
			{
				wordkey[0] = '\0';
				temp = one_argument(temp, wordkey);
				send_buf(wdesc->fd,
						 "%s<TD><A HREF=\"%shelps/%d\">%s</A></TD>\n",
						 (pos == 0) ? "<TR>" : "", DEFAULT_URL_PORT(), count,
						 wordkey);
				if (++pos % 5 == 0)
				{
					send_buf(wdesc->fd, "</TR>");
					pos = 0;
				}
			}
		}
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);
}

void HandleHelpDumpRequest(WEB_DESCRIPTOR * wdesc, HELP_DATA * pHelp)
{
	char buf[MSL * 5];

	print_header(wdesc, pHelp->keyword);
	send_buf(wdesc->fd, "<TABLE>\n");
	send_buf(wdesc->fd, "<TR><TD>[%d] %s<TD></TR>\n", pHelp->level,
			 pHelp->keyword);
	html_colourconv(buf, fix_string(pHelp->text));
	send_buf(wdesc->fd, "<TR><TD>%s</TD></TR>\n", buf);
	send_buf(wdesc->fd, "</TABLE>\n");
	send_buf(wdesc->fd, "<BR><A HREF=\"%shelps\">Back to Help Index</A>\n",
			 DEFAULT_URL_PORT());
	print_footer(wdesc);
}

void HandleRaceRequest(WEB_DESCRIPTOR * wdesc)
{
	int race;

	print_header(wdesc, "Races");
	send_buf(wdesc->fd, "<TABLE><TR>\n");
	send_buf(wdesc->fd, "<TD><I>Race</I></TD>\n");
	send_buf(wdesc->fd,
			 "<TD><I>Str</I></TD><TD><I>Int</I></TD><TD><I>Wis</I></TD><TD><I>Dex</I></TD><TD><I>Con</I></TD><TD><I>Luck</I></TD>\n");
	send_buf(wdesc->fd, "<TD><I>Creation<BR>Points</I></TD></TR>\n");
	for (race = 0; race_table[race].name != NULL; race++)
	{
		if (!race_table[race].pc_race)
			continue;

		send_buf(wdesc->fd, "<TR>\n");
		send_buf(wdesc->fd,
				 "<TD><A HREF=\"%s"
				 "races/%s\">%s</A></TD>\n<TD>%d</TD>\n<TD>%d</TD>\n<TD>%d</TD>\n"
				 "<TD>%d</TD>\n<TD>%d</TD><TD ALIGN=\"center\">%d</TD></TR>\n",
				 DEFAULT_URL_PORT(),
				 race_table[race].name, race_table[race].name,
				 race_table[race].max_stats[STAT_STR],
				 race_table[race].max_stats[STAT_INT],
				 race_table[race].max_stats[STAT_WIS],
				 race_table[race].max_stats[STAT_DEX],
				 race_table[race].max_stats[STAT_CON], race_table[race].points);
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	send_buf(wdesc->fd,
			 "<P>Creation points increase the amount of experience it takes to gain a level. Maximum a stat can go is 30.</P>\n");
	send_buf(wdesc->fd,
			 "<P>To view skills and spells available to each race, visit the <a href=\"%sspells#RaceSkills\">Skill/Spell</A> section.</P>\n",
			 DEFAULT_URL_PORT());
	print_footer(wdesc);
}

void HandleRaceDumpRequest(WEB_DESCRIPTOR * wdesc, int race)
{
	HELP_DATA *pHelp;
	char buf[MSL * 2];

	print_header(wdesc, race_table[race].name);
	sprintf(buf, "%s race", race_table[race].name);
	if ((pHelp = help_lookup(buf)) != NULL)
	{
		html_colourconv(buf, pHelp->text);
		send_buf(wdesc->fd, "<P>%s</P><BR>\n", buf);
	}
	else
		send_buf(wdesc->fd, "<P>No Info Available</P><BR>\n");

	print_footer(wdesc);
}

void HandleClanRequest(WEB_DESCRIPTOR * wdesc)
{
	int clan;
	char buf[MSL * 3];

	print_header(wdesc, "Clans");
	send_buf(wdesc->fd, "<TABLE>\n");
	send_buf(wdesc->fd,
			 "<TR><B><TD><I>Name</I></TD><TD><I>Description</I></TD><TD><I>Homepage</I></TD></TR>\n");
	for (clan = 0; clan < maxClan; clan++)
	{

		html_colourconv(buf, clan_table[clan].who_name);

		send_buf(wdesc->fd, "<TR><TD>%s</TD></TR>\n", buf);
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);

}

void HandleClassRequest(WEB_DESCRIPTOR * wdesc)
{
	int iClass;

	print_header(wdesc, "Classes");
	send_buf(wdesc->fd, "<TABLE>\n");
	for (iClass = 0; iClass < maxClass; iClass++)
	{
		send_buf(wdesc->fd,
				 "<TR><TD><A HREF=\"%sclass/%s\">%s</A>"
				 "</TD></TR>\n", DEFAULT_URL_PORT(),
				 class_table[iClass].name, class_table[iClass].name);
	}
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);
}

void HandleClassDumpRequest(WEB_DESCRIPTOR * wdesc, int Class)
{
	char buf[MSL * 2], buf2[MSL];
	char skill_list[LEVEL_HERO + 1][MSL];
	int snc, lev;
	HELP_DATA *pHelp;

	print_header(wdesc, class_table[Class].name);
	if ((pHelp = help_lookup(class_table[Class].name)) != NULL)
	{
		html_colourconv(buf, pHelp->text);
		send_buf(wdesc->fd, "<TABLE><TR><TD>%s</TD></TR></TABLE>\n", buf);
	}
	send_buf(wdesc->fd, "<TABLE>\n");
	for (lev = 0; lev < LEVEL_HERO + 1; lev++)
		skill_list[lev][0] = '\0';

	for (snc = 0; snc < maxSkill; snc++)
	{
		if (skill_table[snc].name == NULL)
			break;

		if ((lev = skill_table[snc].skill_level[Class]) <= LEVEL_HERO)
		{
			sprintf(buf2, "%s, ", skill_table[snc].name);
			if (IS_NULLSTR(skill_list[lev]))
				sprintf(skill_list[lev], "<TR><TD>Level %d</TD><TD>%s", lev,
						buf2);
			else
				strncat(skill_list[lev], buf2, MSL);
		}
	}
	for (lev = 0; lev < LEVEL_HERO + 1; lev++)
	{
		if (skill_list[lev][0] != '\0')
		{
			if (strlen(skill_list[lev]) > 2)
				skill_list[lev][(strlen(skill_list[lev]) - 2)] = '\0';

			send_buf(wdesc->fd, skill_list[lev]);
			send_buf(wdesc->fd, "</TD></TR>\n");
		}

	}
	send_buf(wdesc->fd, "</TABLE>\n");
	print_footer(wdesc);
}

void HandleNotesRequest(WEB_DESCRIPTOR * wdesc)
{
	int pos = 0;

	print_header(wdesc, "Notes");
	send_buf(wdesc->fd, "<OL>\n");
	for (pos = 0; pos < MAX_BOARD - 3; pos++)
	{
		send_buf(wdesc->fd,
				 "<li><A HREF=\"%snotes/%s\">%s</A>\n", DEFAULT_URL_PORT(),
				 boards[pos].short_name, boards[pos].short_name);
	}
	send_buf(wdesc->fd, "</OL>\n");
	print_footer(wdesc);
}

void HandleNotesDumpRequest(WEB_DESCRIPTOR * wdesc, BOARD_DATA * board)
{
	NOTE_DATA *pnote;
	char buf[MSL * 5];

	sprintf(buf, "Notes on %s Board", board->short_name);
	print_header(wdesc, buf);
	for (pnote = board->note_first; pnote != NULL; pnote = pnote->next)
	{
		if (!is_name(pnote->to_list, "all"))
			continue;

		html_colourconv(buf, pnote->text);
		send_buf(wdesc->fd,
				 "<TABLE><TR><TD><b>%s</b></TD><TD>%s</TD></TR><TR><TD><B>Date</B></TD>\n"
				 "<TD>%s</TD></TR><TR><TD COLSPAN=2>%s</TD><TR></TABLE><BR>\n",
				 pnote->sender, pnote->subject, pnote->date, buf);
	}
	send_buf(wdesc->fd,
			 "<BR><A href=\"%snotes\">Back to Board Index</A>\n",
			 DEFAULT_URL_PORT());
	print_footer(wdesc);
}

void HandleSkDebugRequest(WEB_DESCRIPTOR * wdesc)
{
	int i, sn;

	print_header(wdesc, "Skill List");
	send_buf(wdesc->fd, "<TABLE><TR><TD>Skill Name</TD>\n");
	for (i = 0; i < maxClass; i++)
		send_buf(wdesc->fd, "<TD>%s</TD>\n", class_table[i].name);
	send_buf(wdesc->fd, "</TR>\n");
	for (sn = 0; sn < maxSkill; sn++)
	{
		send_buf(wdesc->fd, "<TR><TD>%s</TD>\n", skill_table[sn].name);
		for (i = 0; i < maxClass; i++)
		{
			if (skill_table[sn].skill_level[i] >= LEVEL_IMMORTAL)
				send_buf(wdesc->fd, "<TD>---</TD>\n");
			else
				send_buf(wdesc->fd, "<TD>%d</TD>\n",
						 skill_table[sn].skill_level[i]);
		}
		send_buf(wdesc->fd, "</TR>\n");
	}
	send_buf(wdesc->fd, "</TABLE>");
	print_footer(wdesc);
}

void HandleLogRequest(WEB_DESCRIPTOR * wdesc)
{
	struct dirent *Dir;
	DIR *Directory;
	char buf[MSL];
	int count = 0;

	print_header(wdesc, "Log Files");
	send_buf(wdesc->fd, "<TABLE><TR>\n");
	Directory = opendir("../log");
	Dir = readdir(Directory);
	while (Dir != NULL)
	{
		if (!str_suffix(".log", Dir->d_name))
		{
			strncpy(buf, Dir->d_name, MSL);
			buf[strlen(buf) - 4] = '\0';
			send_buf(wdesc->fd,
					 "%s<TD><A href=\"%s%s/log/%s\">%s</A></TD>\n",
					 count % 5 == 0 ? "</TR><TR>" : "",
					 DEFAULT_URL_PORT(), SECURE_URL, buf, Dir->d_name);
			count++;
		}
		Dir = readdir(Directory);
	}
	closedir(Directory);
	send_buf(wdesc->fd, "%s</TABLE>\n", count % 5 != 0 ? "</TR>" : "");
	print_footer(wdesc);
}

void HandleLogDumpRequest(WEB_DESCRIPTOR * wdesc, char *file)
{
	char buf[MSL];

	print_header(wdesc, file);
	send_buf(wdesc->fd, "<PRE>\n");
	sprintf(buf, "../log/%s.log", file);
	print_file(wdesc, buf);
	send_buf(wdesc->fd, "</PRE>\n");
	print_footer(wdesc);

}

void HandleObjsRequest(WEB_DESCRIPTOR * wdesc, OBJ_INDEX_DATA * pObj)
{
	char buf[MSL * 3];

	print_header(wdesc, smash_colour(pObj->short_descr));
	html_colourconv(buf, pObj->short_descr);
	send_buf(wdesc->fd, "<P>%s<br>", buf);
	html_colourconv(buf, pObj->description);
	send_buf(wdesc->fd, "%s<br>", buf);
	send_buf(wdesc->fd, "Material: %s<br>", pObj->material);
	send_buf(wdesc->fd, "Type: %s<br></P>", item_name(pObj->item_type));
	print_footer(wdesc);
}

void HandleMobsRequest(WEB_DESCRIPTOR * wdesc, MOB_INDEX_DATA * pMob)
{
	char buf[MSL * 3];

	print_header(wdesc, smash_colour(pMob->short_descr));
	html_colourconv(buf, pMob->short_descr);
	send_buf(wdesc->fd, "<P>%s<br>", buf);
	html_colourconv(buf, pMob->description);
	send_buf(wdesc->fd, "%s<br>", buf);
	send_buf(wdesc->fd, "Race: %s<br>", race_table[pMob->race].name);
	send_buf(wdesc->fd, "Sex: %s<br>", sex_table[pMob->sex].name);
	send_buf(wdesc->fd, "Alignment: %d<br>", pMob->alignment);
	send_buf(wdesc->fd, "</P>");
	print_footer(wdesc);
}

void HandleRoomsRequest(WEB_DESCRIPTOR * wdesc, vnum_t room)
{
	ROOM_INDEX_DATA *pRoom = get_room_index(room);
	EXIT_DATA *pexit;
	int door;
	char buf[MSL * 4];

	if (!pRoom)
		return;

	print_header(wdesc, pRoom->name);
	send_buf(wdesc->fd, "<TABLE><TR><TD colspan=3>");
	html_colourconv(buf, pRoom->name);
	send_buf(wdesc->fd, buf);
	send_buf(wdesc->fd, "</TD><TD colspan=3>%s", pRoom->area->name);
	send_buf(wdesc->fd, "</TD></TR><TR><TD colspan=6>");
	html_colourconv(buf, pRoom->description);
	send_buf(wdesc->fd, buf);
	send_buf(wdesc->fd, "</TD></TR><TR>");
	for (door = 0; door < MAX_DIR; door++)
	{
		if ((pexit = pRoom->exit[door]) != NULL && pexit->u1.to_room != NULL)
		{
			send_buf(wdesc->fd,
					 "<TD><A href=\"%srooms/%ld\">%s</A></TD>",
					 DEFAULT_URL_PORT(), pexit->u1.to_room->vnum,
					 dir_name[door]);
		}
		else
			send_buf(wdesc->fd, "<TD></TD>");
	}
	send_buf(wdesc->fd, "</TR><TR><TD colspan=6>%s</TD>",
			 show_list_to_html(pRoom->first_content));
	send_buf(wdesc->fd, "</TR><TR><TD colspan=6>%s</TD>",
			 show_char_to_html(pRoom->first_person));

	send_buf(wdesc->fd, "</TR></TABLE>");
	print_footer(wdesc);
}

void HandleSocialsRequest(WEB_DESCRIPTOR * wdesc)
{
	int iSocial;

	print_header(wdesc, "Socials");
	send_buf(wdesc->fd, "<TABLE><TR>\n");
	for (iSocial = 0; !IS_NULLSTR(social_table[iSocial].name); iSocial++)
		send_buf(wdesc->fd, "%s<TD>%s</TD>\n",
				 iSocial % 5 == 0 ? "</TR><TR>" : "",
				 social_table[iSocial].name);
	send_buf(wdesc->fd, "%s</TABLE>\n", iSocial % 5 != 0 ? "</TR>" : "");
	print_footer(wdesc);
}

void HandleImmRequest(WEB_DESCRIPTOR * wdesc)
{
	print_header(wdesc, "Immortal Info Page");
	send_buf(wdesc->fd,
			 "<hr><P><A href=\"%s%s/skdebug\">Skill Debug List</A></P>",
			 DEFAULT_URL_PORT(), SECURE_URL);
	send_buf(wdesc->fd, "<P><A href=\"%s%s/log\">Log Files</A></P>",
			 DEFAULT_URL_PORT(), SECURE_URL);
	send_buf(wdesc->fd, "<hr>");
	print_footer(wdesc);
}

void HandleImmInvalid(WEB_DESCRIPTOR * wdesc)
{
	print_header(wdesc, "Invalid Username/Password");
	send_buf(wdesc->fd,
			 "<P>Invalid username/password.  Each field is Case Sensitive.</P>\n");
	print_footer(wdesc);
}

void handle_web_request(WEB_DESCRIPTOR * wdesc)
{
	char tpath[MSL];
	const char *stuff;
	char *path;
	int addr;
	char web_buf[MSL];

	stuff = first_arg(wdesc->request, tpath, FALSE);
	first_arg(stuff, tpath, FALSE);

	path = tpath;
	path++;

	/* process request */
	/* are we using HTTP/1.x? If so, write out header stuff.. */
	if (!strstr(wdesc->request, "GET"))
	{
		send_buf(wdesc->fd, "HTTP/1.1 501 Not Implemented");
		return;
	}
	else if (strstr(wdesc->request, "HTTP/1."))
	{
		if (!str_prefix(SECURE_URL, path))
		{
			char *where;
			char encoded[MIL];
			char username[MIL];
			char *password = &str_empty[0];

			username[0] = '\0';
			encoded[0] = '\0';

			where = strstr(stuff, "Authorization: Basic");

			if (!where)
				send_401UNAUTHORISED(wdesc, AUTH_DOMAIN);
			else
			{
				where += strlen("Authorization: Basic");

				where++;
				for (password = encoded; *where && !isspace(*where);
					 where++, password++)
					*password = *where;

				*password = '\0';

				Base64Decode(encoded, (unsigned char *) username, MIL);

				for (password = username; *password && *password != ':';
					 password++);
				{
					if (*password == ':')
					{
						*password = '\0';
						password++;
					}
				}
			}

			if (check_web_pass(username, password))
			{
				char *buf;

				if (!str_cmp(path, SECURE_URL)
					|| !str_cmp(path, SECURE_URL "/"))
				{
					logf("Web Request: %s - Secure listing", web_buf);
					HandleImmRequest(wdesc);
					return;
				}
				buf = strchr(path, '/');
				if (!IS_NULLSTR(buf))
				{
					buf++;
					if (!str_cmp(buf, "skdebug") || !str_cmp(buf, "skdebug/"))
					{
						logf("Web Request: %s - Skill Debug listing", web_buf);
						HandleSkDebugRequest(wdesc);
						return;
					}
					else if (!str_prefix("log", buf))
					{
						char buf2[MSL];
						char *check;
						DIR *Directory;
						struct dirent *Dir;

						if (!str_cmp(buf, "log") || !str_cmp(buf, "log/"))
						{
							logf("Web Request: %s - Log listing", web_buf);
							HandleLogRequest(wdesc);
							return;
						}
						Directory = opendir("../log");
						Dir = readdir(Directory);
						check = strrchr(buf, '/');
						if (!IS_NULLSTR(check))
						{
							check++;

							while (Dir != NULL)
							{
								if (!str_suffix(".log", Dir->d_name))
								{
									strncpy(buf2, Dir->d_name, MSL);
									buf2[strlen(buf2) - 4] = '\0';
									if (!str_cmp(buf2, check))
									{
										logf("Web Request: Log %s", buf2);
										HandleLogDumpRequest(wdesc, buf2);
										return;
									}
								}
								Dir = readdir(Directory);
							}
						}
						closedir(Directory);
						goto unknown;
					}
				}
				goto unknown;
			}
			else
			{
				HandleImmInvalid(wdesc);
				return;
			}
		}
	}

	addr = ntohl(wdesc->their_addr.sin_addr.s_addr);

	strcpy(web_buf, inet_ntoa(wdesc->their_addr.sin_addr));

	if (!str_cmp(path, "online") || !str_cmp(path, "online/"))
	{
		logf("Web Request: %s - Who listing", web_buf);
		HandleWhoRequest(wdesc);
	}
	else if (!str_cmp(path, "tech") || !str_cmp(path, "tech/"))
	{
		logf("Web Request: %s - Memory status", web_buf);
		HandleMemoryRequest(wdesc);
	}
	else if (!str_cmp(path, "areas") || !str_cmp(path, "areas/"))
	{
		logf("Web Request: %s - Area listing", web_buf);
		HandleAreaRequest(wdesc);
	}
	else if (!str_cmp(path, "spells") || !str_cmp(path, "spells/"))
	{
		logf("Web Request: %s - Spell listing", web_buf);
		HandleSpellsRequest(wdesc);
	}
	else if (!str_cmp(path, "rules") || !str_cmp(path, "rules/"))
	{
		logf("Web Request: %s - Rules listing", web_buf);
		HandleRulesRequest(wdesc);
	}
	else if (!str_cmp(path, "socials") || !str_cmp(path, "socials/"))
	{
		logf("Web Request: %s - Socials", web_buf);
		HandleSocialsRequest(wdesc);
	}
	else if (!str_prefix("helps", path))
	{
		HELP_DATA *pHelp;
		char *buf;
		int pos;
		char temp[MIL];

		if (!str_cmp(path, "helps") || !str_cmp(path, "helps/"))
		{
			logf("Web Request: %s - Help listing", web_buf);
			HandleHelpsRequest(wdesc);
			return;
		}
		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;

			pos = 0;
			for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next)
			{
				pos++;
				sprintf(temp, "%d", pos);
				if (!str_cmp(buf, temp))
				{
					logf("Web Request: %s - Help File %s", pHelp->keyword,
						 web_buf);
					HandleHelpDumpRequest(wdesc, pHelp);
					return;
				}
			}
		}
		goto unknown;
	}
	else if (!str_prefix("notes", path))
	{
		char *buf;
		int pos;

		if (!str_cmp(path, "notes") || !str_cmp(path, "notes/"))
		{
			logf("Web Request: %s - Note listing", web_buf);
			HandleNotesRequest(wdesc);
			return;
		}
		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;

			for (pos = 0; pos < MAX_BOARD - 3; pos++)
			{
				if (!str_cmp(boards[pos].short_name, buf))
				{
					logf("Web Request: %s - Note Board %s", web_buf,
						 boards[pos].short_name);
					HandleNotesDumpRequest(wdesc, &boards[pos]);
					return;
				}
			}
		}
		goto unknown;

	}
	else if (!str_prefix("races", path))
	{
		int race;
		char *buf;

		if (!str_cmp(path, "races") || !str_cmp(path, "races/"))
		{
			logf("Web Request: %s - Race listing", web_buf);
			HandleRaceRequest(wdesc);
			return;
		}
		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;
			for (race = 0; race < maxRace; race++)
			{
				if (!str_cmp(race_table[race].name, buf))
				{
					logf("Web Request: %s - Race %s", web_buf,
						 race_table[race].name);
					HandleRaceDumpRequest(wdesc, race);
					return;
				}
			}
		}
		goto unknown;
	}
	else if (!str_cmp(path, "commands") || !str_cmp(path, "commands/"))
	{
		logf("Web Request: %s - Commands listing", web_buf);
		HandleCommandsRequest(wdesc);
		return;
	}
	else if (!str_prefix("clans", path))
	{
		logf("Web Request: %s - Clan listing", web_buf);
		HandleClanRequest(wdesc);
		return;
	}
	else if (!str_prefix("class", path))
	{
		int i;
		char *buf;

		if (!str_cmp(path, "class") || !str_cmp(path, "class/"))
		{
			logf("Web Request: %s - Class listing", web_buf);
			HandleClassRequest(wdesc);
			return;
		}
		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;
			for (i = 0; i < maxClass; i++)
			{
				if (!str_cmp(class_table[i].name, buf))
				{
					logf("Web Request: %s - Class %s", web_buf,
						 class_table[i].name);
					HandleClassDumpRequest(wdesc, i);
					return;
				}
			}
		}
		goto unknown;
	}
	else if (!str_prefix("chars", path))
	{
		MOB_INDEX_DATA *pMob;
		char *buf;

		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;

			if (!is_number(buf))
			{
				logf("Request: %s - Bad mob vnum", web_buf);
				goto unknown;
			}
			if ((pMob = get_mob_index(atol(buf))) != NULL)
			{
				logf("Web Request: %s - Mob %ld", web_buf, pMob->vnum);
				HandleMobsRequest(wdesc, pMob);
				return;
			}
		}
		goto unknown;
	}
	else if (!str_prefix("objs", path))
	{
		OBJ_INDEX_DATA *pObj;
		char *buf;

		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;

			if (!is_number(buf))
			{
				logf("Request: %s - Bad obj vnum", web_buf);
				goto unknown;
			}
			if ((pObj = get_obj_index(atol(buf))) != NULL)
			{
				logf("Web Request: %s - Obj %ld", web_buf, pObj->vnum);
				HandleObjsRequest(wdesc, pObj);
				return;
			}
		}
		goto unknown;
	}
	else if (!str_prefix("rooms", path))
	{
		ROOM_INDEX_DATA *pRoom;

		if (!str_cmp("rooms", path))
		{
			logf("Web Request: %s - Rooms listing", web_buf);
			HandleRoomsRequest(wdesc, ROOM_VNUM_TEMPLE);
			return;
		}
		else
		{
			char *buf;

			buf = strrchr(path, '/');
			if (!IS_NULLSTR(buf))
			{
				buf++;

				if (!is_number(buf))
				{
					logf("Request: %s - Bad room vnum", web_buf);
					goto unknown;
				}
				if ((pRoom = get_room_index(atol(buf))) != NULL)
				{
					if (!IS_SET
						(pRoom->area->area_flags,
						 AREA_CLOSED | AREA_PLAYER_HOMES))
					{
						logf("Web Request: %s - Room %ld", web_buf,
							 pRoom->vnum);
						HandleRoomsRequest(wdesc, pRoom->vnum);
						return;
					}
				}
			}
			goto unknown;
		}
	}
	else if (!str_prefix("stats", path))
	{
		int pos;
		char *buf;

		if (!str_cmp(path, "stats") || !str_cmp(path, "stats/"))
		{
			logf("Web Request: %s - Stats listing", web_buf);
			HandleStatsRequest(wdesc);
			return;
		}
		buf = strrchr(path, '/');
		if (!IS_NULLSTR(buf))
		{
			buf++;

			for (pos = 0; pos < MAX_GAMESTAT; pos++)
			{
				if (!str_cmp(stat_type_name[pos], buf))
				{
					logf("Web Request: %s - Stats %s", web_buf,
						 stat_type_name[pos]);
					HandleStatsTypeRequest(wdesc, pos);
					return;
				}
			}
		}
		goto unknown;
	}
	else
	{
	  unknown:
		print_header(wdesc, "Error");
		logf("Unknown Request [%s] by %s", path, web_buf);
		send_buf(wdesc->fd, "Unknown url '%s'.<BR>", path);
		print_footer(wdesc);
	}
	tail_chain();
}

void shutdown_web_server(void)
{
	WEB_DESCRIPTOR *current, *next;

	/* Stop Listening */
	log_string("Closing webserver...");
	close(web_socket);
	web_socket = -1;
	WebUP = FALSE;

	/* Close All Current Connections */
	for (current = first_webdesc; current; current = next)
	{
		next = current->next;
		close(current->fd);
		UNLINK(current, first_webdesc, last_webdesc, next, prev);
		free_mem(current);
	}
}