1stMud4.5.3/
1stMud4.5.3/backup/
1stMud4.5.3/bin/
1stMud4.5.3/bin/extras/
1stMud4.5.3/data/i3/
1stMud4.5.3/doc/1stMud/
1stMud4.5.3/doc/Diku/
1stMud4.5.3/doc/MPDocs/
1stMud4.5.3/doc/Rom/
1stMud4.5.3/notes/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
* 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.                                        *
***************************************************************************
* 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.                                           *
* - 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()   *
***************************************************************************
* Additional Modifications based upon with with Insanity Codebase         *
* By Chris Fewtrell (Trax) <C.J.Fewtrell@bcs.org.uk>                      *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "interp.h"
#include "tables.h"
#include "olc.h"
#include "recycle.h"
#include "magic.h"
#include "telnet.h"
#include "data_table.h"
#include "vnums.h"

DataTable wpwd_data_table[] = {
	{"name", FIELD_STRING, (void *) &wpwd_zero.name, NULL, NULL},
	{"pwd", FIELD_STRING, (void *) &wpwd_zero.passw, NULL, NULL},
	{"level", FIELD_INT, (void *) &wpwd_zero.level, NULL, NULL},
	{NULL, (field_t) - 1, NULL, NULL, NULL}
};

TableSave_Fun(rw_wpwd_data)
{
	rw_list(type, WPWD_FILE, WebpassData, wpwd);
}

void update_webpasses(CharData * ch, bool pDelete)
{
	WebpassData *c_next;
	WebpassData *curr;

	if (IsNPC(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, next, prev);
			free_wpwd(curr);
		}
	}
	if (pDelete || NullStr(ch->pcdata->webpass))
	{
		rw_wpwd_data(act_write);
		return;
	}

	curr = new_wpwd();
	replace_str(&curr->name, ch->name);
	replace_str(&curr->passw, ch->pcdata->webpass);
	curr->level = get_trust(ch);
	Link(curr, wpwd, next, prev);
	rw_wpwd_data(act_write);
	return;
}

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

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

	if (!IsImmortal(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';

	if (NullStr(arg1))
	{
		cmd_syntax(ch, NULL, n_fun, "<new>", "show", NULL);
		return;
	}

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

	if (!str_cmp(arg1, "show"))
	{
		chprintlnf(ch, "Your password for the website is: %s",
				   GetStr(ch->pcdata->webpass, "Not Set."));
		return;
	}

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

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

	replace_str(&ch->pcdata->webpass, pwdnew);
	save_char_obj(ch);
	update_webpasses(ch, false);
	chprintln(ch, "Ok.");
	d_write(ch->desc, echo_on_str, 0);
	return;
}

WebpassData *check_web_pass(const char *username, const char *password,
							int level)
{
	WebpassData *current;

	for (current = wpwd_first; current; current = current->next)
		if (current->level >= level && !str_casecmp(current->name, username))
			if (!str_casecmp(current->passw, crypt(password, username)))
				return current;

	return NULL;
}

#ifndef DISABLE_WEBSRV

Proto(bool bind_ip, (SOCKET, int));
Proto(void handle_web_request, (WebDescriptor *));
Proto(int w_print, (WebDescriptor *, const char *));
ProtoF(int w_printf, (WebDescriptor *, const char *, ...), 2, 3);
ProtoF(int w_printlnf, (WebDescriptor *, const char *, ...), 2, 3);
Proto(int w_println, (WebDescriptor *, const char *));
Proto(void handle_web_request, (WebDescriptor *));

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
};

void Base64Decode(char *bufcoded, unsigned char *bufplain, int outbufsize)
{
	int nbytesdecoded;
	int nprbytes;
	char *bufin = bufcoded;
	unsigned char *bufout = bufplain;

	while (*bufcoded == ' ' || *bufcoded == '\t')
		++bufcoded;

	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';
}

struct request_type
{
	char *req;
	char *name;
	Web_F *fun;
	bool secure;
	bool dir;
};

EXTERN const struct request_type request_table[];

#define CONTENT_HTML    1
#define CONTENT_TEXT    2
#define CONTENT_GIF     3
#define CONTENT_JPEG    4
#define CONTENT_GZIP    5
#define CONTENT_WAV     6
#define CONTENT_VRML    7
#define CONTENT_CLASS   8

struct type_data
{
	char *suffix;
	int type;
};

const struct type_data status_types[] = {
	{"Continue", 100},
	{"Switching Protocols", 101},
	{"OK", 200},
	{"Created", 201},
	{"Accepted", 202},
	{"Non-Authoritative Information", 203},
	{"No Content", 204},
	{"Reset Content", 205},
	{"Partial Content", 206},
	{"Multiple Choices", 300},
	{"Moved Permanently", 301},
	{"Found", 302},
	{"See Other", 303},
	{"Not Modified", 304},
	{"Use Proxy", 305},
	{"Temporary Redirect", 307},
	{"Bad Request", 400},
	{"Unauthorized", 401},
	{"Payment Required", 402},
	{"Forbidden", 403},
	{"Not Found", 404},
	{"Method Not Allowed", 405},
	{"Not Acceptable", 406},
	{"Proxy Authentication Required", 407},
	{"Request Time-out", 408},
	{"Conflict", 409},
	{"Gone", 410},
	{"Length Required", 411},
	{"Precondition Failed", 412},
	{"Request Entity Too Large", 413},
	{"Request-URI Too Large", 414},
	{"Unsupported Media Type", 415},
	{"Requested range not satisfiable", 416},
	{"Expectation Failed", 417},
	{"Internal Server Error", 500},
	{"Not Implemented", 501},
	{"Bad Gateway", 502},
	{"Service Unavailable", 503},
	{"Gateway Time-out", 504},
	{"HTTP Version not supported", 505},
	{NULL, 0}
};
const struct type_data content_types[] = {
	{
	 ".html", CONTENT_HTML}
	,
	{
	 ".htm", CONTENT_HTML}
	,
	{
	 ".gif", CONTENT_GIF}
	,
	{
	 ".txt", CONTENT_TEXT}
	,
	{
	 ".text", CONTENT_TEXT}
	,
	{
	 ".jpg", CONTENT_JPEG}
	,
	{
	 ".jpeg", CONTENT_JPEG}
	,
	{
	 ".gz", CONTENT_GZIP}
	,
	{
	 ".gzip", CONTENT_GZIP}
	,
	{
	 ".wav", CONTENT_WAV}
	,
	{
	 ".wrl", CONTENT_VRML}
	,
	{
	 ".class", CONTENT_CLASS}
	,
	{
	 NULL, CONTENT_TEXT}
};

int determine_type(char *path)
{
	int i;

	for (i = 0; content_types[i].suffix != NULL; i++)
	{
		if (!str_suffix(content_types[i].suffix, path))
			return content_types[i].type;
	}

	return CONTENT_TEXT;
}

int determine_status(int status)
{
	int i;

	for (i = 0; status_types[i].type > 0; i++)
	{
		if (status_types[i].type == status)
			return i;
	}
	return -1;
}

int http_error_code = 200;

void http_status(WebDescriptor * wdesc, int status)
{
	int i = determine_status(status);

	if (i != -1)
	{
		w_printlnf(wdesc, "HTTP/1.1 %d %s", status_types[i].type,
				   status_types[i].suffix);
		if (status_types[i].type == 401)
			w_println(wdesc,
					  "WWW-Authenticate: Basic realm='User/Password are Case Sensitive'");
		http_error_code = 200;
	}
}

void send_content(WebDescriptor * wdesc, int type)
{
	switch (type)
	{
		case CONTENT_HTML:
			w_println(wdesc, "Content-type: text/html\n\n");
			break;
		default:
		case CONTENT_TEXT:
			w_println(wdesc, "Content-type: text/plain\n\n");
			break;
		case CONTENT_GIF:
			w_println(wdesc, "Content-type: image/gif\n\n");
			break;
		case CONTENT_WAV:
			w_println(wdesc, "Content-type: audio/x-wav\n\n");
			break;
		case CONTENT_GZIP:
			w_println(wdesc,
					  "Content-type: application/x-zip-compressed\n\n");
			break;
		case CONTENT_VRML:
			w_println(wdesc, "Content-type: x-world/x-vrml\n\n");
			break;
		case CONTENT_CLASS:
			w_println(wdesc, "Content-type: application/octet-stream\n\n");
			break;
	}
}

int w_print(WebDescriptor * wdesc, const char *txt)
{
	if (!wdesc || NullStr(txt))
		return 0;

	return write_to_socket(wdesc->fd, txt, strlen(txt));
}

int w_printf(WebDescriptor * wdesc, const char *fmt, ...)
{
	char buf[MPL];
	va_list args;
	int len;

	if (!wdesc || NullStr(fmt))
		return 0;

	va_start(args, fmt);
	len = vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	return write_to_socket(wdesc->fd, buf, len);
}

int w_println(WebDescriptor * wdesc, const char *buf)
{
	if (!wdesc)
		return 0;

	return (w_print(wdesc, buf) + write_to_socket(wdesc->fd, LF, 1));
}

int w_printlnf(WebDescriptor * wdesc, const char *fmt, ...)
{
	char buf[MPL];

	if (!wdesc)
		return 0;

	if (!NullStr(fmt))
	{
		va_list args;

		va_start(args, fmt);
		vsnprintf(buf, sizeof(buf), fmt, args);
		va_end(args);
	}
	else
		buf[0] = NUL;

	return w_println(wdesc, buf);
}

char html_colorbuf[MSL * 10];

bool lastcolor = false;

const struct htmlcolor_type
{
	color_value_t at;
	char *norm;
	char *bryte;
}
htmlcolor_table[] =
{
	{
	FG_BLACK, "black", "gray"}
	,
	{
	FG_RED, "maroon", "red"}
	,
	{
	FG_GREEN, "green", "lime"}
	,
	{
	FG_YELLOW, "orange", "yellow"}
	,
	{
	FG_BLUE, "navy", "blue"}
	,
	{
	FG_MAGENTA, "magenta", "purple"}
	,
	{
	FG_CYAN, "teal", "cyan"}
	,
	{
	FG_WHITE, "silver", "white"}
	,
	{
	CL_NONE, NULL, NULL}
};

char *htmlcolor_lookup(color_value_t at, bool bryte)
{
	int i;

	for (i = 0; htmlcolor_table[i].at != CL_NONE; i++)
		if (htmlcolor_table[i].at == at)
			return bryte ? htmlcolor_table[i].bryte : htmlcolor_table[i].norm;
	return NULL;
}

char *colortohtml(colatt_t * col)
{
	bool b, script = false;
	char buf[MSL];
	static char out[MSL];
	char *pstr;

	convert_random(col);

	b = (getcol(col, CT_ATTR) == CL_BRIGHT);

	if (VALID_BG(getcol(col, CT_BACK)))
	{
		if (
			(pstr =
			 htmlcolor_lookup((color_value_t) (getcol(col, CT_BACK) - CL_MOD),
							  b)) == NULL)
		{
			script = true;
			pstr = "' + document.bgColor + '";
		}
		sprintf(buf, " background-color: %s;", pstr);

	}

	if (VALID_FG(getcol(col, CT_FORE)))
	{
		if ((pstr = htmlcolor_lookup(getcol(col, CT_FORE), b)) == NULL)
		{
			script = true;
			pstr = "' + document.fgColor + '";
		}

		sprintf(buf, " color: %s;", pstr);
	}

	if (buf[0] != '\0')
	{
		if (script)
			sprintf(out,
					"%s<script type='text/javascript'>document.write('<span style=\"%s\">');</script>",
					lastcolor ? "</span>" : "", buf + 1);
		else
			sprintf(out, "%s<span style=\"%s\">", lastcolor ? "</span>" : "",
					buf + 1);
		lastcolor = true;
	}
	else
		out[0] = '\0';

	return out;
}

int color_slot_lookup(int slot)
{
	int i;

	for (i = 0; i < MAX_CUSTOM_COLOR; i++)
		if (color_table[i].slot == slot)
			return i;

	return -1;
}

char *html_colorconv(const char *txt)
{
	const char *point;
	char *output;
	colatt_t c;
	char buf[MIL];

	lastcolor = false;
	memset(html_colorbuf, 0, sizeof(html_colorbuf));
	output = html_colorbuf;

	init_color_data(&c);

	for (point = txt; *point; point++)
	{
		if (*point == COLORCODE)
		{
			point++;

			if (isdigit(*point))
			{
				int slot = *point - '0';

				if (VALID_CL(slot))
				{
					c[CT_SAVE] = (color_value_t) (slot);
				}

				if (*(point + 1) == '+' && *(point + 2))
				{
					point += 2;
				}
			}

			else if (*point == '=')
			{
				point++;
				c[CT_SAVE] = (color_value_t) (c[CT_FORE]);
			}

			switch (*point)
			{
				case '}':
					strcpy(buf, "<br>");
					break;
				case '-':
					strcpy(buf, "~");
					break;
				case 'n':

					strcpy(buf, mud_info.name);
					break;
				case 'N':

					strcpy(buf, strupper(mud_info.name));
					break;
				case 't':
				case 'T':

					if (*(point + 1) == '+' && *(point + 2) == '"'
						&& *(point + 3))
					{
						char fmt[800];
						size_t t = 0;

						point += 3;

						do
						{
							fmt[t++] = *point++;
						}
						while (*point && *point != '"' && t < sizeof(fmt));

						fmt[t] = '\0';

						strcpy(buf, str_time(-1, -1, fmt));
					}
					else
						strcpy(buf, str_time(-1, -1, NULL));
					break;
				case 'P':
				case 'p':
					break;
				case COLORCODE:
					sprintf(buf, "%c", COLORCODE);
					break;
				case 'X':
				case 'x':
					init_color_data(&c);
					sprintf(buf,
							"%s<script type='text/javascript'>document.write('<span style=\"color: ' + document.fgColor + ';\">');</script>",
							lastcolor ? "</span>" : "");
					break;
				default:
					set_col_attr(*point, &c, NULL);
					strcpy(buf, colortohtml(&c));
					break;
			}
			add_text(buf, output);
		}
		else if (*point == CUSTOMSTART)
		{
			int slot = _NONE, pos;

			do
			{
				point++;
				if (isdigit(*point))
				{
					if (slot == _NONE)
						slot = 0;
					slot = (slot * 10) + (*point - '0');
				}
			}
			while (*point && *point != CUSTOMEND);

			if (!VALID_COLOR(slot) || (pos = color_slot_lookup(slot)) == -1)
			{
				bug("invalid custom color");
				strcpy(buf, " ");
			}
			else
			{
				copy_color(c, color_table[pos].col_attr);
				strcpy(buf, colortohtml(&c));
			}
			add_text(buf, output);
		}
		else if (*point == '<')
		{
			strcpy(buf, HTML_LT);
			add_text(buf, output);
		}
		else if (*point == '>')
		{
			strcpy(buf, HTML_GT);
			add_text(buf, output);
		}
		else if (*point == '"')
		{
			strcpy(buf, HTML_QUOTE);
			add_text(buf, output);
		}
		else if (*point == '&')
		{
			strcpy(buf, HTML_AMP);
			add_text(buf, output);
		}
		else if (*point == '\n')
		{
			strcpy(buf, "<br>");
			add_text(buf, output);
		}
		else
		{
			*output++ = *point;
		}
	}

	if (lastcolor == true)
	{
		char *end = "</span>";

		add_text(end, output);
	}

	*output = '\0';
	return html_colorbuf;
}

#define CHAR_URL_PREFIX    	    	"chars"
#define OBJ_URL_PREFIX    	    	"objs"
#define ROOM_URL_PREFIX    	    	"rooms"
#define HELP_URL_PREFIX    	    	"helps"
#define NOTE_URL_PREFIX    	    	"notes"
#define RACE_URL_PREFIX    	    	"races"
#define DEITY_URL_PREFIX    	"deity"
#define CMD_URL_PREFIX    	    "cmds"
#define CLAN_URL_PREFIX    	    	"clans"
#define CLASS_URL_PREFIX    	"class"
#define STAT_URL_PREFIX    	    	"stats"
#define ADMIN_URL_PREFIX    	"admin"
#define LOGIN_URL_PREFIX    	"login"
#define NAMEGEN_URL_PREFIX  "namegen"
#define SONG_URL_PREFIX    	    	"songs"
#define CHANGE_URL_PREFIX   "changes"

const char *HTTP_URL(const char *fmt, ...)
{
	static char buf[5][MIL];
	char path[MIL];
	char *result;
	static int i;

	path[0] = NUL;
	if (!NullStr(fmt))
	{
		va_list args;

		va_start(args, fmt);
		vsnprintf(path, sizeof(path), fmt, args);
		va_end(args);
	}

	++i;
	i %= 5;
	result = buf[i];
	sprintf(result, "http://%s:%d/%s", HOSTNAME, webport, path);
	return result;
}

const char *html_link(const char *desc, const char *fmt, ...)
{
	static char buf[5][MIL];
	char path[MIL];
	char *result, url[MIL];
	static int i;

	path[0] = NUL;
	if (!NullStr(fmt))
	{
		va_list args;

		va_start(args, fmt);
		vsnprintf(path, sizeof(path), fmt, args);
		va_end(args);
	}

	++i;
	i %= 5;
	result = buf[i];
	sprintf(url, "http://%s:%d/%s", HOSTNAME, webport, path);
	sprintf(result, "<a href=\"%s\">%s</a>", url,
			!NullStr(desc) ? desc : !NullStr(path) ? path : url);
	return result;
}

const char *get_next(const char *path, char *base)
{
	static char *buf[5];
	static int i;
	int x;

	if (NullStr(path))
		return path;

	++i, i %= 5;

	buf[i] = strchr(path, '/');

	base[0] = NUL;
	if (!NullStr(buf[i]))
	{
		for (x = path[0] == '/' ? 1 : 0; path[x] != NUL; x++)
		{
			if (path[x] == buf[i][0] && !str_cmp(&path[x], buf[i]))
			{
				base[x] = NUL;
				break;
			}
			base[x] = path[x];
		}
		if (buf[i][0] == '/')
			buf[i]++;
	}

	return buf[i];
}

const char *get_prev(const char *path, char *base)
{
	static char *buf[5];
	static int i;
	int x;

	if (NullStr(path))
		return path;

	++i, i %= 5;

	buf[i] = strrchr(path, '/');

	base[0] = NUL;
	if (!NullStr(buf[i]))
	{
		for (x = path[0] == '/' ? 1 : 0; path[x] != NUL; x++)
		{
			if (path[x] == buf[i][0] && !str_cmp(&path[x], buf[i]))
			{
				base[x] = NUL;
				break;
			}
			base[x] = path[x];
		}
		if (buf[i][0] == '/')
			buf[i]++;
	}

	return buf[i];
}

WebpassData *get_name_password(WebDescriptor * wdesc, int level)
{
	char *where;
	char encoded[MIL];
	char username[MIL];
	char *password = &str_empty[0];

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

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

	if (!where)
	{
		http_status(wdesc, 401);
	}
	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++;
			}
		}
	}

	return check_web_pass(username, password, level);
}

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 = tolower(*point);
		}
	}
	return;
}

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

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

int meta_refresh = -1;
const char *meta_location = NULL;

void print_header(WebDescriptor * wdesc, const char *title)
{
	int i;

	http_status(wdesc, http_error_code);
	send_content(wdesc, CONTENT_HTML);
	w_println(wdesc, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">");
	w_println(wdesc, "<html><head>");
	w_println(wdesc,
			  "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>");
	if (meta_refresh > -1)
	{
		w_printlnf(wdesc, "<meta http-equiv='refresh' content='%d;url=%s'",
				   meta_refresh, meta_location);
		meta_refresh = -1;
	}
	w_printlnf(wdesc, "<title>");
	if (!NullStr(title))
		w_printlnf(wdesc, "%s: %s", mud_info.name, title);
	else
		w_printlnf(wdesc, "%s Homepage", mud_info.name);
	w_println(wdesc, "</title>");
	w_println(wdesc,
			  "<style>body { background-color: #000033; color: #FFCC99; font-family: ");
	w_println(wdesc,
			  "Verdana sans-serif; font-size: 12px; letter-spacing: 1px; height: 500;}");
	w_println(wdesc, "a { color: #FFCC99; text-decoration: none;}");
	w_println(wdesc,
			  "a:link, a:visited { border-bottom: solid thin #336699; }");
	w_println(wdesc,
			  "a:hover, a:active { border-bottom: solid thin #993333; }");
	w_println(wdesc,
			  ".head { background-color: #336699; color: #003366; border: thin solid #336699; }");
	w_println(wdesc, ".head-left { font-size: 20px; }");
	w_println(wdesc,
			  ".head-right { font-size: 26px;text-align: right; font-weight: bold; color: #000033; }");
	w_println(wdesc,
			  "hr { border: thin solid #000033; color: #336699; width: 100%; height: 3mm; }");
	w_println(wdesc, "img { border: none; }");
	w_println(wdesc,
			  ".h4 { background-color: #336699; color: #000033; font-size: 16px; font-weight: 700; ");
	w_println(wdesc,
			  "border: solid thin #000033; padding: 2px 0px 1px 1cm; word-spacing: 2px; width: 100%; }");
	w_println(wdesc,
			  ".main {background-color: #003366; padding: 5mm 5mm 5mm 5mm; border-top: 0mm; ");
	w_println(wdesc,
			  "border-left: 2mm; border-bottom: 2mm; border-right: 2mm; border-style: solid; ");
	w_println(wdesc, "border-color: #336699; height: 100%; }");
	w_println(wdesc, ".menu {float: left; padding-right: 1cm; }");
	w_println(wdesc,
			  ".footer { background-color: #336699; color: #003366; border: thin solid #336699; ");
	w_println(wdesc, "padding: 0px; } .small { 10px; }");
	w_println(wdesc, ".footer-left { text-align: left; }");
	w_println(wdesc, ".footer-right { text-align: right; }");
	w_println(wdesc, ".addr { text-align: right; font-size: 9px; }");
	w_println(wdesc, ".all { line-height: 150%; }");
	w_println(wdesc,
			  "dt { font-weight: bold; } .pre { white-space: pre; font-family: monospace; }");
	w_println(wdesc, "</style></head><body>");
	w_println(wdesc, "<div class=\"all\">");
	w_println(wdesc, "<div class=\"head\">");
	w_println(wdesc,
			  "<table width=\"98%\"><tr><td class=\"head-left\">&nbsp;");
	if (!NullStr(title))
		w_println(wdesc, title);
	else
		w_println(wdesc, "Welcome");
	w_printlnf(wdesc, "</td><td class=\"head-right\">...%s</td></tr></table>",
			   mud_info.name);
	w_println(wdesc, "</div><div class=\"main\">");

	w_println(wdesc,
			  "<div class=\"menu\"><table><tr><td class=\"footer-left\">Contents</td></tr>");
	for (i = 0; request_table[i].req != NULL; i++)
	{
		if (!request_table[i].name)
			continue;

		w_printlnf(wdesc,
				   "<tr><td class=\"footer-left\"><a %shref='%s'>%s</a></td></tr>",
				   request_table[i].secure ? "class=\"small\" " : "",
				   HTTP_URL(request_table[i].req), request_table[i].name);
	}
	w_println(wdesc, "</table></div>");
}

void print_footer(WebDescriptor * wdesc)
{
	w_println(wdesc, "</div><div class=\"footer\">");
	w_println(wdesc, "<table width=\"98%\"><tr>");

	w_println(wdesc, "<td class=\"footer-left\"><table><tr><td>");
	w_printlnf(wdesc, "[<a href=\"%s/\">Home</a>] ", HTTP_URL(NULL));
	w_println(wdesc, "[<a href=\"#\" onclick=\"history.back()\">Back</a>] ");
	w_println(wdesc,
			  "[<a href=\"#\" onclick=\"history.forward()\">Forward</a>] ");
	w_println(wdesc,
			  "[<a href=\"#\" onclick=\"location.reload()\">Refresh</a>]</td></tr>");
	w_println(wdesc,
			  "<tr><td><a href=\"http://validator.w3.org/check/referer\">"
			  "<img src=\"http://www.w3.org/Icons/valid-html401\" alt=\"Valid HTML 4.0.1!\" height=\"31\" width=\"88\">");
	w_printlnf(wdesc,
			   "</a> <a href=\"http://jigsaw.w3.org/css-validator/validator?uri=%s\">",
			   HTTP_URL(NULL));
	w_println(wdesc,
			  "<img width=\"88\" height=\"31\" src=\"http://jigsaw.w3.org/css-validator/images/vcss\" alt=\"Valid CSS!\"></a>");
	w_println(wdesc, "</td></tr></table></td>");

	w_println(wdesc,
			  "<td class=\"footer-right\"><table><tr><td><b>1stMud Server Project Ring</b></td></tr>");
	w_println(wdesc,
			  "<tr><td>[ <a href=\"http://C.webring.com/wrman?ring=1stmudserverproj;addsite\">Join</a>");
	w_println(wdesc,
			  " | <a href=\"http://C.webring.com/hub?ring=1stmudserverproj;id=3;hub\">List</a> | ");
	w_println(wdesc,
			  "<a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;random\">Random</a> | ");
	w_println(wdesc,
			  "<b><a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;prev\">&lt;&lt; Prev</a>");
	w_println(wdesc,
			  "| <a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;next\">Next &gt;&gt;</a>");
	w_println(wdesc, "</b> ]</td></tr></table></td>");

	w_printlnf(wdesc,
			   "</tr></table></div><div class=\"addr\"><span>&copy; <a href=\"mailto:%s@%s?re:%s\">%s@%s</a>",
			   UNAME, HOSTNAME, mud_info.name, UNAME, HOSTNAME);
	w_println(wdesc, "</span></div></div></body></html>");
}

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

	if ((fp = f_open(filename, "r")) != NULL)
	{
		while (!f_eof(fp))
		{
			while ((buf[num] = f_getc(fp)) != EOF && buf[num] != '\n'
				   && buf[num] != '\r' && num < (MSL - 2))
				num++;
			c = f_getc(fp);
			if ((c != '\n' && c != '\r') || c == buf[num])
				f_ungetc(c, fp);
			buf[num] = '\0';
			w_println(wdesc, buf);
			num = 0;
		}
		f_close(fp);
	}
}

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

	output[0] = '\0';

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

	if (IsObjStat(obj, ITEM_INVIS))
		strcat(output, "(Invis) ");
	if (IsObjStat(obj, ITEM_DARK))
		strcat(output, "(Dark) ");
	if (IsObjStat(obj, ITEM_EVIL))
		strcat(output, "(Red Aura) ");
	if (IsObjStat(obj, ITEM_BLESS))
		strcat(output, "(Blue Aura) ");
	if (IsObjStat(obj, ITEM_MAGIC))
		strcat(output, "(Magical) ");
	if (IsObjStat(obj, ITEM_GLOW))
		strcat(output, "(Glowing) ");
	if (IsObjStat(obj, ITEM_HUM))
		strcat(output, "(Humming) ");

	if (obj->item_type == ITEM_WEAPON)
	{
		for (paf = obj->affect_first; paf; paf = paf->next)
		{
			if (IsSet(paf->bitvector, WEAPON_FLAMING))
				strcat(output, "(Flaming) ");
			if (IsSet(paf->bitvector, WEAPON_FROST))
				strcat(output, "(Frost) ");
			if (IsSet(paf->bitvector, WEAPON_VAMPIRIC))
				strcat(output, "(Vampiric) ");
			if (IsSet(paf->bitvector, WEAPON_SHOCKING))
				strcat(output, "(Shocking) ");
			if (IsSet(paf->bitvector, WEAPON_POISON))
				strcat(output, "(Poison) ");
			if (IsSet(paf->bitvector, WEAPON_SHARP))
				strcat(output, "(Sharp) ");
			if (IsSet(paf->bitvector, WEAPON_VORPAL))
				strcat(output, "(Vorpal) ");
		}
	}

	if (IsObjStat(obj, ITEM_AUCTIONED))
		strcat(output, "(Auctioned) ");

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

	if (obj->description != NULL)
	{
		sprintf(buf, "<a href='%s'>%s</a><br>",
				HTTP_URL(OBJ_URL_PREFIX "/%ld", obj->pIndexData->vnum),
				html_colorconv(obj->description));
		strcat(output, buf);
	}

	return output;
}

char *show_list_to_html(ObjData * list)
{
	static char output[MSL * 6];
	const char **prgpstrShow;
	int *prgnShow;
	char *pstrShow;
	ObjData *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 (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(CharData * victim)
{
	static char buf[MSL * 6];
	char temp[MSL];

	buf[0] = '\0';

	if (IsAffected(victim, AFF_INVISIBLE))
		strcat(buf, "(Invis) ");
	if (victim->invis_level >= LEVEL_IMMORTAL)
		strcat(buf, "(Wizi) ");
	if (IsAffected(victim, AFF_HIDE))
		strcat(buf, "(Hide) ");
	if (IsAffected(victim, AFF_CHARM))
		strcat(buf, "(Charmed) ");
	if (IsAffected(victim, AFF_PASS_DOOR))
		strcat(buf, "(Translucent) ");
	if (IsAffected(victim, AFF_FAERIE_FIRE))
		strcat(buf, "(Pink Aura) ");
	if (IsEvil(victim))
		strcat(buf, "(Red Aura) ");
	if (IsGood(victim))
		strcat(buf, "(Golden Aura) ");
	if (IsAffected(victim, AFF_SANCTUARY))
		strcat(buf, "(White Aura) ");
	if (!IsNPC(victim) && !victim->desc)
		strcat(buf, "(Linkdead) ");

	if (!IsNPC(victim))
	{
		if (IsSet(victim->comm, COMM_AFK))
			strcat(buf, "*AFK* ");
		if (victim->war)
			strcat(buf, "(War) ");
		if (!IsNPC(victim) && IsSet(victim->act, PLR_OUTLAW))
			strcat(buf, "(OUTLAW) ");
		if (IsSet(victim->comm, COMM_QUIET))
			strcat(buf, "[QUIET] ");
		if (victim->desc && victim->desc->editor != ED_NONE)
			strcat(buf, "[OLC] ");
		if (victim->pcdata->in_progress != NULL)
			strcat(buf, "[Note] ");
		if (IsQuester(victim))
			strcat(buf, "[Q] ");
	}

	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, "(DEAD) ");
		else if (vict_condition < 33)
			strcat(buf, "(Wounded) ");
	}

	if (victim->position == (IsNPC(victim) ? victim->start_pos : POS_STANDING)
		&& !NullStr(victim->long_descr))
	{
		if (IsNPC(victim))
		{
			sprintf(temp, "<a href='%s'>%s</a>",
					HTTP_URL(CHAR_URL_PREFIX "/%ld",
							 victim->pIndexData->vnum),
					html_colorconv(victim->long_descr));
			strcat(buf, temp);
		}
		return (buf);
	}
	if (IsNPC(victim))
	{
		sprintf(temp, "<a href='%s'>%s</a>",
				HTTP_URL(CHAR_URL_PREFIX "/%ld", victim->pIndexData->vnum),
				html_colorconv(victim->short_descr));
		strcat(buf, temp);
	}
	else
	{
		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 (IsSet(victim->on->value[2], SLEEP_AT))
				{
					sprintf(temp, " is sleeping at %s.",
							victim->on->short_descr);
					strcat(buf, temp);
				}
				else if (IsSet(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 (IsSet(victim->on->value[2], REST_AT))
				{
					sprintf(temp, " is resting at %s.",
							victim->on->short_descr);
					strcat(buf, temp);
				}
				else if (IsSet(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 (IsSet(victim->on->value[2], SIT_AT))
				{
					sprintf(temp, " is sitting at %s.",
							victim->on->short_descr);
					strcat(buf, temp);
				}
				else if (IsSet(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 (IsSet(victim->on->value[2], STAND_AT))
				{
					sprintf(temp, " is standing at %s.",
							victim->on->short_descr);
					strcat(buf, temp);
				}
				else if (IsSet(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,
					   IsNPC(victim) ? victim->fighting->
					   short_descr : victim->fighting->name);
				strcat(buf, ".");
			}
			else
				strcat(buf, "someone who left??");
			break;
		default:
			break;
	}
	return (buf);
}

char *show_char_to_html(CharData * list)
{
	CharData *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);
}

Web_Fun(HandleMemoryRequest)
{
	int sn, count_spell = 0, count_skill = 0;

	for (sn = 0; sn < top_skill; 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");
	w_println(wdesc, "<table><tr>");
	w_printlnf(wdesc, "<table><tr><td>Version</td><td>%s</td></tr>",
			   MUDSTRING);
	w_println(wdesc, "<tr><td>Compiled on</td><td>" __DATE__ "</td><tr>");
	w_printlnf(wdesc, "<td>Affects</td>  <td>%d</td></tr>", top_affect);
	w_printlnf(wdesc, "<tr><td>Areas</td> <td>%d</td></tr>", top_area);
	w_printlnf(wdesc, "<tr><td>Extra Descriptions</td> <td>%d</td></tr>",
			   top_ed);
	w_printlnf(wdesc, "<tr><td>Exits</td> <td>%d</td></tr>", top_exit);
	w_printlnf(wdesc, "<tr><td>Helps</td> <td>%d</td></tr>", top_help);
	w_printlnf(wdesc, "<tr><td>Socials</td> <td>%d</td></tr>", top_social);
	w_printlnf(wdesc, "<tr><td>Spells</td> <td>%d</td></tr>", count_spell);
	w_printlnf(wdesc, "<tr><td>Spell Groups</td><td>%d</td></tr>", top_group);
	w_printlnf(wdesc, "<tr><td>Skills</td> <td>%d</td></tr>", count_skill);
	w_printlnf(wdesc, "<tr><td>Mobiles</td> <td>%d (%d in use)</td></tr>",
			   top_char_index, mobile_count);
	w_printlnf(wdesc, "<tr><td>Objects</td> <td>%d (%d in use)</td></tr>",
			   top_obj_index, top_obj);
	w_printlnf(wdesc, "<tr><td>Resets</td> <td>%d</td></tr>", top_reset);
	w_printlnf(wdesc, "<tr><td>Rooms</td> <td>%d (%d explorable)</td></tr>",
			   top_room_index, top_explored);
	w_printlnf(wdesc, "<tr><td>Shops</td> <td>%d</td></tr>", top_shop);
	w_printlnf(wdesc, "<tr><td>Number of Players</td><td>%d</td></tr>",
			   pfiles.count);
	w_printlnf(wdesc, "<tr><td>Races</td><td>%d</td></tr>", top_race);
	w_printlnf(wdesc, "<tr><td>Programs (mob,obj,room)</td><td>%d</td></tr>",
			   top_mprog + top_oprog + top_rprog);
	w_printlnf(wdesc, "<tr><td>Bans</td><td>%d</td></tr>", top_ban);
	w_printlnf(wdesc, "<tr><td>Deities</td><td>%d</td></tr>", top_deity);
	w_printlnf(wdesc, "<tr><td>Clans</td><td>%d (%d members)</td></tr>",
			   top_clan, top_mbr);
	w_printlnf(wdesc, "<tr><td>Commands</td><td>%d</td></tr>", top_cmd);
	w_printlnf(wdesc, "<tr><td>Classes</td><td>%d</td></tr>", top_class);
	w_printlnf(wdesc, "<tr><td>Channels</td><td>%d</td></tr>", top_channel);
	w_printlnf(wdesc, "<tr><td>Notes</td><td>%d</td></tr>", top_note);
	w_printlnf(wdesc, "<tr><td>MSP Sounds</td><td>%d</td></tr>", top_msp);
	w_printlnf(wdesc, "<tr><td>String Space</td><td>%d (%s)</td></tr>",
			   nAllocString, fsize(sAllocString));
	w_println(wdesc, "</table>");
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleRulesRequest)
{
	HelpData *pHelp;

	print_header(wdesc, "Rules");
	w_println(wdesc, "<p>");
	if ((pHelp = help_lookup("RULES")) != NULL)
	{
		w_println(wdesc, html_colorconv(help_text(pHelp->text)));
	}
	else
		w_println(wdesc, "There are no rules!! Anarchy!!");
	w_println(wdesc, "</p>");
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleCreditsRequest)
{
	HelpData *pHelp;

	print_header(wdesc, "Credits");
	if ((pHelp = help_lookup("DIKU")) != NULL)
	{
		w_println(wdesc, "<hr><p>");
		w_println(wdesc, html_colorconv(help_text(pHelp->text)));
		w_println(wdesc, "</p>");
	}
	if ((pHelp = help_lookup("MERC")) != NULL)
	{
		w_println(wdesc, "<hr><p>");
		w_println(wdesc, html_colorconv(help_text(pHelp->text)));
		w_println(wdesc, "</p>");
	}
	if ((pHelp = help_lookup("ROM CREDITS")) != NULL)
	{
		w_println(wdesc, "<hr><p>");
		w_println(wdesc, html_colorconv(help_text(pHelp->text)));
		w_println(wdesc, "</p>");
	}
	if ((pHelp = help_lookup("1STMUD CREDITS")) != NULL)
	{
		w_println(wdesc, "<hr><p>");
		w_println(wdesc, html_colorconv(help_text(pHelp->text)));
		w_println(wdesc, "</p>");
	}
	else
	{
		w_printlnf(wdesc, "<hr><table><tr><td>%s (%s) design by:</td>",
				   mud_info.name, MUDSTRING);
		w_printlnf(wdesc,
				   "<td>Markanth</td><td>&lt;markanth@firstmud.com&gt;</td></tr>");
	}
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleAreaRequest)
{
	AreaData *pArea;
	int count = 0;

	print_header(wdesc, "Areas");
	w_println(wdesc, "<table>");
	w_println(wdesc, "<tr>");
	w_println(wdesc, "<th>Low/High Level</th>");
	w_println(wdesc, "<th>Author</th>");
	w_println(wdesc, "<th>Area Name</th></tr>");
	for (pArea = area_first_sorted; pArea; pArea = pArea->next_sort)
	{
		if (!IsSet(pArea->area_flags, AREA_CLOSED | AREA_PLAYER_HOMES))
		{
			w_println(wdesc, "<tr>");
			if (!NullStr(pArea->lvl_comment))
				w_printlnf(wdesc, "<td>%s</td>", pArea->lvl_comment);
			else
				w_printlnf(wdesc, "<td>%03d %03d</td>", pArea->min_level,
						   pArea->max_level);
			w_printlnf(wdesc, "<td>%s</td>", pArea->credits);
			w_printlnf(wdesc, "<td>%s</td></tr>", pArea->name);
			count++;
		}
	}
	w_println(wdesc, "</table>");
	w_printlnf(wdesc, "<p>Areas Found: %d.</p>", count);
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleSpellsRequest)
{
	int gn;
	int sn, i;
	int tn;
	RaceData *race;
	bool *displayed;
	int past_default = true;
	char buf[MSL];
	char buf2[MSL];
	char buf3[MSL];

	alloc_mem(displayed, bool, top_skill);
	memset(displayed, false, top_skill);

	print_header(wdesc, "Spells and Skills");

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

		if (!str_cmp(group_table[gn].name, "rom basics"))
		{
			w_println(wdesc, "<H3>Basic Skills</H3>");
			w_println(wdesc,
					  "All players receive the <I>basic</I> skills for their class.<br>");
			w_println(wdesc, "<table><tr>");
			w_println(wdesc, "<td>Basic Group</td>");
			w_println(wdesc, "<td>Skills Included</td></tr>");
		}

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

			past_default = false;
			w_println(wdesc, "</table>");
			w_println(wdesc, "<H3>Default Skill/Spell Groups</H3>");
			w_println(wdesc,
					  "Players receive the <I>default</I> skills and spell groups if they bypass customization.<br>");
			w_println(wdesc, "<table><tr>");
			w_println(wdesc, "<td>Default Group</td>");
			w_println(wdesc, "<td>Skills Included</td>");
			w_println(wdesc, "<td>Skill/Spell Groups Included</td></tr>");
		}

		if (!str_cmp(group_table[gn].name, "weaponsmaster"))
		{
			past_default = true;
			w_println(wdesc, "</table>");
			w_println(wdesc, "<H3>Other Skill/Spell Groups</H3>");
			w_println(wdesc, "Players may gain these skills and spells.<br>");
			w_println(wdesc, "<table><tr>");
			w_println(wdesc, "<td>Skill/Spell Group</td>");
			w_println(wdesc, "<td>Skills/Spells Included</td></tr>");
		}

		under_line(buf3, group_table[gn].name);
		w_printlnf(wdesc, "<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)
			{
				sprintf(buf2, "%s, ", group_table[gn].spells[sn]);
				strcat(buf, buf2);

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

		else
			strcpy(buf, "None");

		if (!past_default)
		{
			w_printlnf(wdesc, "%s</td><td>", 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]);
					sprintf(buf2, "<a href='#%s'>%s</a>, ", buf3,
							group_table[gn].spells[sn]);
					strcat(buf, buf2);
				}
				else
				{

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

			else
				strcpy(buf, "None");
		}
		w_printlnf(wdesc, "%s</td></tr>", buf);
	}
	w_println(wdesc, "</table>");

	w_println(wdesc, "<H3>Other Skills and Spells</H3>");
	w_println(wdesc,
			  "The following skills and spells are available to various mortals.<br>");
	w_println(wdesc, "<table><tr><td>");
	buf[0] = '\0';

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

		if (!displayed[sn] && (min_class_level(sn) < ANGEL))
		{
			sprintf(buf2, "%s, ", skill_table[sn].name);
			strcat(buf, buf2);
			displayed[sn] = true;
		}
	}
	if (!NullStr(buf) && (strlen(buf) > 2))
		buf[(strlen(buf) - 2)] = '\0';

	else
		strcpy(buf, "None");
	w_printlnf(wdesc, "%s</td></tr>", buf);
	w_println(wdesc, "</table>");

	w_printlnf(wdesc,
			   "<p>To view skills in class specific tables, visit the <a href='%s'>Classes</a> section.</p>",
			   HTTP_URL(CLASS_URL_PREFIX));

	w_println(wdesc, "<H3><a NAME='RaceSkills'>Race Skills</a></H3>");
	w_println(wdesc, "The following skills and spells are race specific.");

	w_println(wdesc, "<table><tr>");

	w_println(wdesc, "<td>Race</td>");
	w_println(wdesc, "<td>Skills</td></tr>");

	for (race = race_first; race; race = race->next)
	{
		if (!race->pc_race)
			continue;
		w_printlnf(wdesc, "<tr><td>%s</td><td>", race->name);
		buf[0] = '\0';
		for (sn = 1; sn < top_skill; sn++)
		{
			if (skill_table[sn].name == NULL)
				break;
			for (i = 0; i < MAX_RACE_SKILL; i++)
			{
				if (race->skills[i] == NULL)
					break;
				if (skill_lookup(race->skills[i]) == sn)
				{
					sprintf(buf2, "%s, ", skill_table[sn].name);
					strcat(buf, buf2);
					displayed[sn] = true;
				}
			}
		}
		if (!NullStr(buf) && (strlen(buf) > 2))
			buf[(strlen(buf) - 2)] = '\0';

		else
			strcpy(buf, "None");
		w_printlnf(wdesc, "%s</td></tr>", buf);
	}
	w_println(wdesc, "</table>");

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

	w_println(wdesc, "This list includes some spells under development.<br>");
	w_println(wdesc, "<table><tr>");

	w_println(wdesc, "<td>Spells</td></tr>");
	w_println(wdesc, "<tr><td>");
	buf[0] = '\0';
	for (sn = 1; sn < top_skill; sn++)
	{
		if (skill_table[sn].name == NULL)
			break;
		if (!displayed[sn])
		{
			sprintf(buf2, "%s, ", skill_table[sn].name);
			strcat(buf, buf2);
			displayed[sn] = true;
		}
	}
	if (!NullStr(buf) && (strlen(buf) > 2))
		buf[(strlen(buf) - 2)] = '\0';

	else
		strcpy(buf, "None");
	w_printlnf(wdesc, "%s</td></tr>", buf);
	w_println(wdesc, "</table>");
	print_footer(wdesc);
	free_mem(displayed);
	return true;
}

Web_Fun(HandleWhoRequest)
{
	CharData *wch;

	print_header(wdesc, "Players currently");
	w_println(wdesc, "<table><tr>");
	w_println(wdesc,
			  "<th>Level</th><th>Race</th><th>Class</th><th>Clan</th>");
	w_println(wdesc, "<th>Name</th></tr>");
	for (wch = player_first; wch != NULL; wch = wch->next_player)
	{
		if (wch->invis_level >= LEVEL_IMMORTAL
			|| wch->incog_level >= LEVEL_IMMORTAL)
			continue;

		w_println(wdesc, "<tr>");
		if (NullStr(wch->pcdata->who_descr))
		{
			w_printlnf(wdesc, "<td>%d</td>", wch->level);
			w_printlnf(wdesc, "<td><a href='%s'>%s</a></td>",
					   HTTP_URL(RACE_URL_PREFIX "/%s", wch->race->name),
					   wch->race->name);
			w_printlnf(wdesc, "<td>%s</td>", class_short(wch));
		}
		else
		{
			w_printlnf(wdesc, "<td colspan=3>%s</td>",
					   html_colorconv(wch->pcdata->who_descr));
		}

		if (is_clan(wch))
		{
			w_printlnf(wdesc, "<td><a href='%s'>%s</a></td>",
					   HTTP_URL(CLAN_URL_PREFIX "/%s", CharClan(wch)->name),
					   html_colorconv(CharClan(wch)->who_name));
		}
		else
			w_println(wdesc, "<td></td>");

		w_println(wdesc, "<td>");
		if (NullStr(wch->pcdata->email))
			w_println(wdesc, wch->name);
		else
			w_printlnf(wdesc, "<a href=\"mailto://%s?subject=%s\">%s</a>",
					   wch->pcdata->email, wch->name);
		w_println(wdesc, html_colorconv(wch->pcdata->title));
		w_println(wdesc, "</td></tr>");

	}
	w_println(wdesc, "</table><br>");
	print_footer(wdesc);
	return true;
}

const char *stat_type_name[MAX_GAMESTAT + 1][2] = {

	{"GENERAL STATS", "general"},
	{"PLAYER KILLERS", "pkill"},
	{"MOB KILLERS", "mkill"},
	{"PK DEATHS", "pkdead"},
	{"MOB DEATHS", "mdead"}
};

Web_Fun(HandleStatsRequest)
{
	int pos;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Stats");
		w_println(wdesc, "<ul>");
		for (pos = 0; pos <= MAX_GAMESTAT; pos++)
		{
			w_printlnf(wdesc, "<li><a href='%s'>%s</a>",
					   HTTP_URL(STAT_URL_PREFIX "/%s",
								stat_type_name[pos][1]),
					   stat_type_name[pos][0]);
		}
		w_println(wdesc, "</ul>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		if (!str_cmp("general", wdesc->buf))
		{
			print_header(wdesc, "General Stats");
			w_println(wdesc, "<table><tr>");
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Logins",
					   mud_info.stats.logins);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Quests",
					   mud_info.stats.quests);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Quests Complete", mud_info.stats.qcomplete);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Levels",
					   mud_info.stats.levels);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>", "Newbies",
					   mud_info.stats.newbies);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Deletions",
					   mud_info.stats.deletions);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Mob Deaths", mud_info.stats.mobdeaths);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Auctions",
					   mud_info.stats.auctions);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Auctions Sold", mud_info.stats.aucsold);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Player Deaths",
					   mud_info.stats.pdied);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Player Kills", mud_info.stats.pkill);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Notes",
					   mud_info.stats.notes);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>", "Remorts",
					   mud_info.stats.remorts);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Wars",
					   mud_info.stats.wars);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Global Quests", mud_info.stats.gquests);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Connections",
					   mud_info.stats.connections);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>",
					   "Connects this Boot", mud_info.stats.boot_connects);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Online Record",
					   mud_info.stats.online);
			w_printlnf(wdesc, "</tr><tr><td>%s</td><td>%ld</td>", "Web Hits",
					   mud_info.stats.web_requests);
			w_printlnf(wdesc, "<td>%s</td><td>%ld</td>", "Channel Messages",
					   mud_info.stats.chan_msgs);
			w_println(wdesc, "</tr></table>");
			w_printlnf(wdesc, "<p>Stats since: %s</p>",
					   timestr(current_time - mud_info.stats.lastupdate,
							   false));
			print_footer(wdesc);
			return true;
		}
		for (pos = 1; pos <= MAX_GAMESTAT; pos++)
		{
			if (!str_cmp(stat_type_name[pos][1], wdesc->buf))
			{
				Proto(int compare_stats, (const void *, const void *));
				char temp[MSL];
				StatData *curr;
				StatData **top;
				int count, loop;
				bool found = false;
				EXTERN int compare_type;

				sprintf(temp, "Ranking of %s", stat_type_name[pos][0]);
				print_header(wdesc, temp);
				w_println(wdesc, "<table>");

				alloc_mem(top, StatData *, top_stat);

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

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

				w_println(wdesc,
						  "<tr><th>Rank</th><th>Name</th><th>Number</th>"
						  "<th>Rank</th><th>Name</th><th>Number</th></tr>");
				for (loop = 0; loop < count; loop++)
				{
					if (loop >= 50)
						break;

					w_printlnf(wdesc,
							   "%s<td>%2d)</td><td>%-20s</td><td>%ld</td>",
							   pos == 0 ? "<tr>" : "", loop + 1,
							   top[loop]->name,
							   top[loop]->gamestat[compare_type]);

					if (++pos % 2 == 0)
					{
						w_println(wdesc, "</tr>");
						pos = 0;
					}

				}
				if (!found)
					w_println(wdesc,
							  "<tr><td colspan=3>No one found yet.</td></tr>");

				w_println(wdesc, "</table>");

				print_footer(wdesc);
				free_mem(top);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleCommandsRequest)
{
	CmdData *i;
	int pos = 0;
	HelpData *pHelp;
	int count = 0;

	print_header(wdesc, "Commands");
	w_println(wdesc, "<table>");

	for (i = cmd_first_sorted; i; i = i->next_sort)
	{
		if (i->level >= LEVEL_IMMORTAL || i->category == CMDCAT_NOSHOW)
			continue;
		count = 0;
		for (pHelp = help_first; pHelp; pHelp = pHelp->next)
		{
			count++;
			if (is_name(i->name, pHelp->keyword))
				break;
		}
		if (pHelp)
			w_printlnf(wdesc, "%s<td><a href='%s'>%s</td>",
					   (pos == 0) ? "<tr>" : "",
					   HTTP_URL(HELP_URL_PREFIX "/%d", count), i->name);
		else
			w_printlnf(wdesc, "%s<td>%s</td>", (pos == 0) ? "<tr>" : "",
					   i->name);
		if (++pos % 5 == 0)
		{
			w_println(wdesc, "</tr>");
			pos = 0;
		}
	}
	w_println(wdesc, "</table>");
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleHelpsRequest)
{
	HelpData *pHelp;
	int pos = 0;

	if (NullStr(wdesc->buf))
	{
		int count = 0;

		print_header(wdesc, "Help Files");
		w_println(wdesc, "<table>");
		for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next)
		{
			count++;
			if (pHelp->level <= MAX_MORTAL_LEVEL && pHelp->level >= 0)
			{
				const char *temp;
				char wordkey[MSL];

				temp = pHelp->keyword;
				while (!NullStr(temp))
				{
					wordkey[0] = '\0';
					temp = one_argument(temp, wordkey);
					w_printlnf(wdesc, "%s<td><a href='%s'>%s</a></td>",
							   (pos == 0) ? "<tr>" : "",
							   HTTP_URL(HELP_URL_PREFIX "/%d", count),
							   wordkey);
					if (++pos % 5 == 0)
					{
						w_println(wdesc, "</tr>");
						pos = 0;
					}
				}
			}
		}
		w_println(wdesc, "</table>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		char temp[MIL];

		pos = 0;
		for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next)
		{
			pos++;
			sprintf(temp, "%d", pos);
			if (!str_cmp(wdesc->buf, temp))
			{
				print_header(wdesc, pHelp->keyword);
				w_println(wdesc, "<table>");
				w_printlnf(wdesc,
						   "<tr><td>[%d] %s<td><td>Category: %s</td></tr>",
						   pHelp->level, pHelp->keyword,
						   flag_string(help_types, pHelp->category));

				w_printlnf(wdesc, "<tr><td colspan=\"2\">%s</td></tr>",
						   html_colorconv(fix_string(pHelp->text)));
				w_println(wdesc, "</table>");

				print_footer(wdesc);
				return true;
			}
		}
	}
	print_header(wdesc, "Can't find help");
	w_printlnf(wdesc, "<p>Can't find help %s</p>", wdesc->buf);
	print_footer(wdesc);
	return true;

	return false;
}

Web_Fun(HandleDeityRequest)
{
	DeityData *d;
	int e;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Deities");
		w_println(wdesc,
				  "<table><tr><th>Name</th><th>Ethos</th><th>Description</th></tr>");
		for (e = ETHOS_LAWFUL_GOOD; e != ETHOS_CHAOTIC_EVIL; e--)
			for (d = deity_first; d; d = d->next)
			{
				if (d->ethos != (ethos_t) e)
					continue;

				w_printlnf(wdesc,
						   "<tr><td>%s</td><td>%s</td><td>%s</td></tr>",
						   d->name, flag_string(ethos_types, d->ethos),
						   d->desc);
			}
		w_println(wdesc, "</table>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (d = deity_first; d; d = d->next)
		{
			if (!str_cmp(d->name, wdesc->buf))
			{
				HelpData *pHelp;

				print_header(wdesc, d->name);
				if ((pHelp = help_lookup(d->name)) != NULL)
				{
					w_printlnf(wdesc, "<p>%s</p><br>",
							   html_colorconv(pHelp->text));
				}
				else
					w_println(wdesc, "<p>No Info Available</p><br>");

				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleRaceRequest)
{
	RaceData *race;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Races");
		w_println(wdesc, "<table><tr>");
		w_println(wdesc, "<th>Race</th>");
		w_printlnf(wdesc,
				   "<th>Str</th><th>Int</th><th>Wis</th><th>Dex</th><th>Con</th>");
		w_println(wdesc, "<th>Creation<br>Points</th></tr>");
		for (race = race_first; race != NULL; race = race->next)
		{
			if (!race->pc_race)
				continue;

			w_println(wdesc, "<tr>");
			w_printlnf(wdesc,
					   "<td><a href='%s" "'>%s</a></td>" LF "<td>%d</td>" LF
					   "<td>%d</td>" LF "<td>%d</td>" LF "<td>%d</td>" LF
					   "<td>%d</td><td align='center'>%d</td></tr>",
					   HTTP_URL(RACE_URL_PREFIX "/%s", race->name),
					   race->name, race->max_stats[STAT_STR],
					   race->max_stats[STAT_INT], race->max_stats[STAT_WIS],
					   race->max_stats[STAT_DEX], race->max_stats[STAT_CON],
					   race->points);
		}
		w_println(wdesc, "</table>");
		w_printlnf(wdesc,
				   "<p>Creation points increase the amount of experience it takes to gain a level. Maximum a stat can go is 30.</p>");
		w_printlnf(wdesc,
				   "<p>To view skills and spells available to each race, visit the <a href='%s'>Skill/Spell</a> section.</p>",
				   HTTP_URL("spells#RaceSkills"));
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (race = race_first; race; race = race->next)
		{
			if (!str_cmp(race->name, wdesc->buf))
			{
				HelpData *pHelp;

				print_header(wdesc, race->name);
				if ((pHelp = help_lookup(race->name)) != NULL)
				{
					w_printlnf(wdesc, "<p>%s</p><br>",
							   html_colorconv(pHelp->text));
				}
				else
					w_println(wdesc, "<p>No Info Available</p><br>");

				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleClanRequest)
{
	ClanData *clan;
	char i[MSL], j[MSL];
	ClanMember *pmbr;
	int e;

	print_header(wdesc, "Clans");
	w_println(wdesc, "<table>");
	w_println(wdesc,
			  "<tr><th>Name</th><th>Ethos</th><th>Description</th><th>Leaders</th></tr>");
	for (e = ETHOS_LAWFUL_GOOD; e != ETHOS_CHAOTIC_EVIL; e--)
		for (clan = clan_first; clan; clan = clan->next)
		{
			if (clan->ethos != (ethos_t) e)
				continue;

			i[0] = '\0';
			j[0] = '\0';
			for (pmbr = mbr_first; pmbr != NULL; pmbr = pmbr->next)
			{
				if (pmbr->clan != clan || pmbr->rank != (MAX_RANK - 1))
					continue;
				sprintf(j, " %s,", pmbr->name);
				strcat(i, j);
			}
			if (!NullStr(i))
			{
				i[strlen(i) - 1] = '\0';
				w_printlnf(wdesc,
						   "<tr><td>%s%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
						   html_colorconv(clan->who_name),
						   flag_string(ethos_types, clan->ethos),
						   html_colorconv(clan->description), i);
			}
			else
				w_printlnf(wdesc,
						   "<tr><td>%s</td><td>%s</td><td>%s</td><td>None</td></tr>",
						   html_colorconv(clan->who_name),
						   flag_string(ethos_types, clan->ethos),
						   html_colorconv(clan->description));
		}
	w_println(wdesc, "</table>");
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleClassRequest)
{
	int i;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Classes");
		w_println(wdesc, "<table>");
		w_println(wdesc, "<tr><th>Name</th><th>Description</th></tr>");
		for (i = 0; i < top_class; i++)
		{
			w_printlnf(wdesc,
					   "<tr><td><a href='%s'>%s</a>" "</td><td>%s</td></tr>",
					   HTTP_URL(CLASS_URL_PREFIX "/%s",
								class_table[i].name[0]),
					   class_table[i].name[0], class_table[i].description);
		}
		w_println(wdesc, "</table>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (i = 0; i < top_class; i++)
		{
			if (!str_cmp(class_table[i].name[0], wdesc->buf))
			{
				char buf3[MSL];
				char skill_list[MAX_MORTAL_LEVEL + 1][MSL];
				int snc, lev;
				HelpData *pHelp;

				print_header(wdesc, class_table[i].name[0]);
				if ((pHelp = help_lookup(class_table[i].name[0])) != NULL)
				{
					w_printlnf(wdesc, "<table><tr><td>%s</td></tr></table>",
							   html_colorconv(pHelp->text));
				}
				w_println(wdesc, "<table>");
				for (lev = 0; lev < MAX_MORTAL_LEVEL + 1; lev++)
					skill_list[lev][0] = '\0';

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

					if ((lev = skill_table[snc].skill_level[i]) <=
						MAX_MORTAL_LEVEL)
					{
						sprintf(buf3, "%s, ", skill_table[snc].name);
						if (NullStr(skill_list[lev]))
							sprintf(skill_list[lev],
									"<tr><td>Level %d</td><td>%s", lev, buf3);
						else
							strcat(skill_list[lev], buf3);
					}
				}
				for (lev = 0; lev < MAX_MORTAL_LEVEL + 1; lev++)
				{
					if (skill_list[lev][0] != '\0')
					{
						if (strlen(skill_list[lev]) > 2)
							skill_list[lev][(strlen(skill_list[lev]) - 2)] =
								'\0';

						w_println(wdesc, skill_list[lev]);
						w_println(wdesc, "</td></tr>");
					}

				}
				w_println(wdesc, "</table>");
				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleNotesRequest)
{
	int pos = 0;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Notes");
		w_println(wdesc, "<ul>");
		for (pos = 0; pos < MAX_BOARD; pos++)
		{
			if (IsSet(boards[pos].flags, BOARD_NOWEB))
				continue;

			w_printlnf(wdesc, "<li><a href='%s'>%s</a>",
					   HTTP_URL(NOTE_URL_PREFIX "/%s",
								boards[pos].short_name),
					   boards[pos].short_name);
		}
		w_println(wdesc, "</ul>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (pos = 0; pos < MAX_BOARD; pos++)
		{
			if (IsSet(boards[pos].flags, BOARD_NOWEB))
				continue;
			if (!str_cmp(boards[pos].short_name, wdesc->buf))
			{
				BoardData *board = &boards[pos];
				NoteData *pnote;
				char buf2[MSL * 5];

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

					w_printlnf(wdesc,
							   "<table><tr><td>From: %s</td><td>To: %s</td></tr><tr><td>Subject: %s</td>"
							   "<td>Date: %s</td></tr>", pnote->sender,
							   pnote->to_list, pnote->subject, pnote->date);
					w_printlnf(wdesc,
							   "<tr><td colspan=2><hr>%s<hr></td><tr></table><br>",
							   html_colorconv(pnote->text));
				}

				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleObjsRequest)
{
	ObjIndex *pObj;

	if (!NullStr(wdesc->buf))
	{
		if (!is_number(wdesc->buf))
		{
			return false;
		}
		if ((pObj = get_obj_index(atov(wdesc->buf))) != NULL)
		{
			print_header(wdesc, strip_color(pObj->short_descr));
			w_printlnf(wdesc, "<p>%s<br>", html_colorconv(pObj->short_descr));
			w_printlnf(wdesc, "%s<br>", html_colorconv(pObj->description));
			w_printlnf(wdesc, "Material: %s<br>", pObj->material);
			w_printlnf(wdesc, "Type: %s<br></p>",
					   flag_string(type_flags, pObj->item_type));
			print_footer(wdesc);
			return true;
		}
	}
	return false;
}

Web_Fun(HandleMobsRequest)
{
	CharIndex *pMob;

	if (!NullStr(wdesc->buf))
	{
		if (!is_number(wdesc->buf))
		{
			return false;
		}
		if ((pMob = get_char_index(atov(wdesc->buf))) != NULL)
		{
			print_header(wdesc, strip_color(pMob->short_descr));
			w_printlnf(wdesc, "<p>%s<br>", html_colorconv(pMob->short_descr));
			w_printlnf(wdesc, "%s<br>", html_colorconv(pMob->description));
			w_printlnf(wdesc, "Race: %s<br>", pMob->race->name);
			w_printlnf(wdesc, "Sex: %s<br>",
					   flag_string(sex_flags, pMob->sex));
			w_printlnf(wdesc, "Alignment: %d<br>", pMob->alignment);
			w_println(wdesc, "</p>");
			print_footer(wdesc);
			return true;
		}
	}
	return false;
}

char *make_exit_link(ExitData * pexit, int door)
{
	return FORMATF("<a href='%s'>%s</a>",
				   HTTP_URL(ROOM_URL_PREFIX "/%ld", pexit->u1.to_room->vnum),
				   dir_name[door]);
}

void HandleRoomExit(WebDescriptor * wdesc, const char *td1, const char *td2,
					const char *td3)
{
	w_printlnf(wdesc,
			   "<td align=\"right\">%s</td><td align=\"center\">%s</td><td align=\"left\">%s</td></tr><tr>",
			   td1, td2, td3);
}

Web_Fun(HandleRoomsRequest)
{
	RoomIndex *pRoom;
	vnum_t vnum;

	if (NullStr(wdesc->buf))
	{
		vnum = ROOM_VNUM_TEMPLE;
	}
	else
	{
		if (!is_number(wdesc->buf))
		{
			return false;
		}
		vnum = atov(wdesc->buf);
	}
	if ((pRoom = get_room_index(vnum)) != NULL)
	{
		if (!IsSet(pRoom->area->area_flags, AREA_CLOSED | AREA_PLAYER_HOMES))
		{
			ExitData *pexit, *pexit2;
			int door, d;
			bool found = false;
			const int dirs[MAX_DIR] =
				{ DIR_UP, DIR_NORTH, DIR_EAST, DIR_WEST, DIR_SOUTH,
				DIR_DOWN
			};

			print_header(wdesc, pRoom->name);
			w_println(wdesc, "<table><tr><td alig=\"left\">");
			w_println(wdesc, html_colorconv(pRoom->name));
			w_printlnf(wdesc, "</td><td align=\"right\">%s",
					   pRoom->area->name);
			w_printlnf(wdesc, "</td></tr><tr><td align=\"left\">");
			w_println(wdesc, html_colorconv(pRoom->description));
			w_println(wdesc, "</td><td align=\"right\"><table><tr>");

			for (d = 0; d < MAX_DIR; d++)
			{
				door = dirs[d];
				if ((pexit = pRoom->exit[door]) != NULL
					&& pexit->u1.to_room != NULL)
				{
					switch (door)
					{
						default:
							HandleRoomExit(wdesc, "",
										   make_exit_link(pexit, door), "");
							break;

						case DIR_EAST:
							if (!found)
							{
								if ((pexit2 = pRoom->exit[rev_dir[door]])
									&& pexit2->u1.to_room)
								{
									HandleRoomExit(wdesc,
												   make_exit_link(pexit,
																  door), "",
												   make_exit_link(pexit2,
																  rev_dir
																  [door]));
								}
								else
									HandleRoomExit(wdesc,
												   make_exit_link(pexit,
																  door), "",
												   "");
								found = true;
							}
							break;
						case DIR_WEST:
							if (!found)
							{
								if ((pexit2 = pRoom->exit[rev_dir[door]])
									&& pexit2->u1.to_room)
								{
									HandleRoomExit(wdesc,
												   make_exit_link(pexit2,
																  rev_dir
																  [door]), "",
												   make_exit_link(pexit,
																  door));
								}
								else
									HandleRoomExit(wdesc, "", "",
												   make_exit_link(pexit,
																  door));
								found = true;
							}
							break;
					}
				}
				else
					w_println(wdesc, "<td colspan=\"3\"></td></tr><tr>");
			}
			w_printlnf(wdesc,
					   "</tr></table></tr><tr><td colspan=\"2\">%s</td>",
					   show_list_to_html(pRoom->content_first));
			w_printlnf(wdesc, "</tr><tr><td colspan=\"2\">%s</td>",
					   show_char_to_html(pRoom->person_first));

			w_println(wdesc, "</tr></table>");
			print_footer(wdesc);
			return true;
		}
	}
	return false;
}

Web_Fun(HandleChangesRequest)
{
	int i;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Recent Changes");
		w_println(wdesc, "<table><tr><th>Date</th><th>Change</th></tr>");
		for (i = 0; i < top_change; i++)
		{
			w_printlnf(wdesc, "<tr><td>%d)</td>", i + 1);
			w_printlnf(wdesc, "<td><I>%s</I></td><td>%s</td></tr>",
					   str_time(change_table[i].time, -1, "%D"),
					   html_colorconv(change_table[i].change));
		}
		w_println(wdesc, "</table>");
		w_printlnf(wdesc, "<p>%d changes found.</p>", top_change);
		w_printlnf(wdesc, "<p>Check <a href=\"%s\">old</a> changes.</p>\n",
				   HTTP_URL(CHANGE_URL_PREFIX "/old"));
		print_footer(wdesc);
		return true;
	}
	else if (!str_prefix("old", wdesc->buf))
	{
		struct dirent **clist;
		int i = 0, o;
		int count = 0;

		if (!str_cmp(wdesc->buf, "old") || !str_cmp(wdesc->buf, "old/"))
		{
			print_header(wdesc, "Changes History");
			o = scandir(DATA_DIR "oldchanges/", &clist, 0, alphasort);
			for (i = 0; i != o; i++)
			{
				if (!str_prefix("changes", clist[i]->d_name))
				{
					if (count == 0)
					{
						w_println(wdesc, "<ul>");
					}
					w_printlnf(wdesc, "<li><a href=\"%s\">%s</a>\n",
							   HTTP_URL(CHANGE_URL_PREFIX "/old/%s",
										clist[i]->d_name), clist[i]->d_name);
					count++;
				}
			}
			if (count == 0)
				w_println(wdesc, "<li>No old changes available.</ul>");
			else
				w_println(wdesc, "</ul>");
			print_footer(wdesc);
			return true;
		}
		wdesc->buf = get_next(wdesc->buf, wdesc->baseurl);
		if (!NullStr(wdesc->buf))
		{
			o = scandir(DATA_DIR "oldchanges/", &clist, 0, alphasort);
			for (i = 0; i != o; i++)
			{

				if (!str_prefix("changes.", clist[i]->d_name)
					&& !str_cmp(wdesc->buf, clist[i]->d_name))
				{
					ChangeData *old_change_table = NULL, old_change_zero;
					int top_old_change = 0, c;
					DataTable *old_change_data_table = change_data_table;
					char file[MIL];

					sprintf(file, DATA_DIR "oldchanges/%s", clist[i]->d_name);
					rw_table(act_read, file, ChangeData, old_change);

					count++;
					print_header(wdesc, FORMATF("Changes History %d"));
					w_println(wdesc, "<table>");
					w_printf(wdesc,
							 "<tr><th>Date</th><th>Change</th></tr>\n");
					for (c = 0; c < top_old_change; c++)
					{
						w_printlnf(wdesc, "<tr><td>%d)</td>", c + 1);
						w_printlnf(wdesc,
								   "<td><I>%s</I></td><td>%s</td></tr>",
								   str_time(old_change_table[c].time, -1,
											"%D"),
								   html_colorconv(old_change_table
												  [c].change));
						free_string(old_change_table[c].change);
						free_string(old_change_table[c].coder);
					}
					w_println(wdesc, "</table>");
					w_printlnf(wdesc, "<p>%d changes found.</p>",
							   top_old_change);
					free_mem(old_change_table);
					print_footer(wdesc);
					return true;
				}
			}
		}
	}
	return false;
}

Web_Fun(HandleSocialsRequest)
{
	SocialData *iSocial;
	int i = 0;

	print_header(wdesc, "Socials");
	w_println(wdesc, "<table><tr>");
	for (iSocial = social_first; iSocial; iSocial = iSocial->next)
		w_printlnf(wdesc, "%s<td>%s</td>", i++ % 5 == 0 ? "</tr><tr>" : "",
				   iSocial->name);
	w_printlnf(wdesc, "%s</table>", i % 5 != 0 ? "</tr>" : "");
	print_footer(wdesc);
	return true;
}

crs_t crs_lookup(const char *name)
{
	switch (strswitch(name, "copyover", "reboot", "shutdown", NULL))
	{
		default:
			return CRS_NONE;
		case 1:
			return CRS_COPYOVER;
		case 2:
			return CRS_REBOOT;
		case 3:
			return CRS_SHUTDOWN;
	}
}

void web_login_menu(WebDescriptor * wdesc, WebpassData * info)
{
	w_printlnf(wdesc,
			   "<hr><p>Welcome %s! You security level is %d.</p><ul><li><a href=\"%s\">Change password</a>",
			   info->name, info->level,
			   HTTP_URL(LOGIN_URL_PREFIX "/changepass"));
	w_println(wdesc, "</ul><hr>");
}

Web_Fun(HandleImmRequest)
{
	WebpassData *info;

	if ((info = get_name_password(wdesc, LEVEL_IMMORTAL)) != NULL)
	{
		if (NullStr(wdesc->buf))
		{
			print_header(wdesc, "Immortal Info Page");
			web_login_menu(wdesc, info);
			w_printlnf(wdesc,
					   "<ul><li><a href='%s'>Skill Debug List</a></li>",
					   HTTP_URL(ADMIN_URL_PREFIX "/skdebug"));
			w_printlnf(wdesc, "<li><a href='%s'>Log Files</a></li>",
					   HTTP_URL(ADMIN_URL_PREFIX "/log"));
			w_printlnf(wdesc,
					   "<li><a href=\"%s\">Immortal Help Files</a></li>",
					   HTTP_URL(ADMIN_URL_PREFIX "/ihelps"));
			w_printlnf(wdesc, "<li><a href=\"%s\">Webserver Users</a></li>",
					   HTTP_URL(ADMIN_URL_PREFIX "/users"));
			w_printlnf(wdesc,
					   "<li>Game Control<ul><li><a href=\"%s\">Copyover</a></li>",
					   HTTP_URL(ADMIN_URL_PREFIX "/copyover"));
			w_printlnf(wdesc,
					   "<li><a href=\"%s\">Reboot</a></li><li><a href=\"%s\">Shutdown</a></li></ul>",
					   HTTP_URL(ADMIN_URL_PREFIX "/reboot"),
					   HTTP_URL(ADMIN_URL_PREFIX "/shutdown"));
			w_println(wdesc, "<hr>");
			print_footer(wdesc);
			return true;
		}
		else
		{
			crs_t stat;

			if (!str_prefix("skdebug", wdesc->buf))
			{
				int i, sn;

				print_header(wdesc, "Skill List");
				w_println(wdesc, "<table><tr><td>Skill Name</td>");
				for (i = 0; i < top_class; i++)
					w_printlnf(wdesc, "<td>%s</td>", class_table[i].name[0]);
				w_println(wdesc, "</tr>");
				for (sn = 0; sn < top_skill; sn++)
				{
					w_printlnf(wdesc, "<tr><td>%s</td>",
							   skill_table[sn].name);
					for (i = 0; i < top_class; i++)
					{
						if (skill_table[sn].skill_level[i] >= LEVEL_IMMORTAL)
							w_println(wdesc, "<td>---</td>");
						else
							w_printlnf(wdesc, "<td>%d</td>",
									   skill_table[sn].skill_level[i]);
					}
					w_println(wdesc, "</tr>");
				}
				w_println(wdesc, "</table>");
				print_footer(wdesc);
				return true;
			}
			else if (!str_prefix("users", wdesc->buf))
			{
				WebpassData *pwd;

				print_header(wdesc, "WebServer Users");
				if (!wpwd_first)
					w_println(wdesc, "<p><B>No users at this time.</B></p>");
				else
				{
					w_printlnf(wdesc,
							   "<table><tr><th>Name</th><th>Level</th></tr>");
					for (pwd = wpwd_first; pwd; pwd = pwd->next)
						w_printlnf(wdesc,
								   "<tr><td><a href=\"%s\">%s</a></td><td>%d</td></tr>",
								   HTTP_URL(LOGIN_URL_PREFIX "/changepass"),
								   pwd->name, pwd->level);
					w_println(wdesc, "</table>");
				}
				print_footer(wdesc);
				return true;
			}
			else if (!str_prefix("log", wdesc->buf))
			{
				char buf2[MSL];
				struct dirent **Dir;
				int o, i;

				wdesc->buf = get_next(wdesc->buf, wdesc->baseurl);

				if (NullStr(wdesc->buf))
				{
					char buff[MSL];
					int count = 0;

					print_header(wdesc, "Log Files");
					w_println(wdesc, "<table><tr>");
					o = scandir(LOG_DIR, &Dir, 0, alphasort);
					for (i = 0; i != o; i++)
					{
						if (!str_suffix(".log", Dir[i]->d_name))
						{
							strcpy(buff, Dir[i]->d_name);
							buff[strlen(buff) - 4] = '\0';
							w_printlnf(wdesc,
									   "%s<td><a href='%s'>%s</a></td>",
									   count % 5 == 0 ? "</tr><tr>" : "",
									   HTTP_URL(ADMIN_URL_PREFIX "/log/%s",
												buff), Dir[i]->d_name);
							count++;
						}
					}
					w_printlnf(wdesc, "%s</table>",
							   count % 5 != 0 ? "</tr>" : "");
					print_footer(wdesc);
					return true;
				}
				else
				{
					o = scandir(LOG_DIR, &Dir, 0, alphasort);
					for (i = 0; i != o; i++)
					{
						if (!str_suffix(".log", Dir[i]->d_name))
						{
							strcpy(buf2, Dir[i]->d_name);
							buf2[strlen(buf2) - 4] = '\0';
							if (!str_cmp(buf2, wdesc->buf))
							{
								print_header(wdesc, buf2);
								w_println(wdesc, "<p class=\"pre\">");
								sprintf(buf2, LOG_DIR "%s", Dir[i]->d_name);
								print_file(wdesc, buf2);
								w_println(wdesc, "</p>");
								print_footer(wdesc);
								return true;
							}
						}
					}
				}
				return false;
			}
			else if (!str_prefix("ihelps", wdesc->buf))
			{
				HelpData *pHelp;
				int count = 0, check = 0;

				print_header(wdesc, "Immortal Help Files");
				w_println(wdesc, "<table>");
				for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next)
				{
					count++;
					if (pHelp->level >= LEVEL_IMMORTAL || pHelp->level < 0)
					{
						const char *temp2;
						char wordkey[MSL];

						temp2 = pHelp->keyword;
						while (!NullStr(temp2))
						{
							wordkey[0] = NUL;
							temp2 = one_argument(temp2, wordkey);
							w_printlnf(wdesc,
									   "%s<td><a href=\"%s\">%s</a></td>\n",
									   (check == 0) ? "<tr>" : "",
									   HTTP_URL(HELP_URL_PREFIX "/%d", count),
									   wordkey);
							if (++check % 5 == 0)
							{
								w_print(wdesc, "</tr>");
								check = 0;
							}
						}
					}
				}
				w_println(wdesc, "</table>");
				print_footer(wdesc);
				return true;
			}
			else if ((stat = crs_lookup(wdesc->buf)) != CRS_NONE)
			{
				Proto(const char *crs_type_name, (crs_t));
				ProtoNoR(void crs_fun, (crs_t));

				meta_refresh = 5;
				meta_location = HTTP_URL(NULL);
				print_header(wdesc,
							 FORMATF("Sending %s Request...",
									 crs_type_name(stat)));
				w_printlnf(wdesc, "Sending %s Request now... please wait.",
						   crs_type_name(stat));
				print_footer(wdesc);
				crs_info.timer = 1;
				crs_info.status = stat;
				replace_str(&crs_info.who, "an admin web request");
				crs_fun(stat);
			}
		}
		return false;
	}
	else
	{
		print_header(wdesc, "Invalid Username/Password");
		w_printlnf(wdesc,
				   "<p>You're Username and Password should be your,"
				   " Character name on the mud, and the password you set\n"
				   " with the 'webpass' command.  Each field is Case Sensitive.</p>");
		print_footer(wdesc);
		return true;
	}
}

Web_Fun(HandlePlayersRequest)
{
	WebpassData *info;

	if ((info = get_name_password(wdesc, 1)))
	{
		if (NullStr(wdesc->buf))
		{
			print_header(wdesc, "Players Page");
			w_printlnf(wdesc,
					   "<blockquote>Welcome %s, I'm glad you were able to logon!"
					   "  In a continuing effort to bring players of %s"
					   " closer together, you"
					   " can now mark your place on our website.  Choose one of the options below."
					   "</blockquote>", info->name, mud_info.name);
			web_login_menu(wdesc, info);
			print_footer(wdesc);
			return true;
		}
		else
		{

			if (!str_prefix("changepass", wdesc->buf))
			{
				WebpassData *wp;
				char *opass, *npass, *cpass, *uname;

				if (!str_cmp(wdesc->buf, "changepass")
					|| !str_cmp(wdesc->buf, "changepass/"))
				{
					print_header(wdesc,
								 FORMATF("Change password for %s",
										 info->name));
					w_printlnf(wdesc, "<form method=\"get\" action=\"%s\">",
							   HTTP_URL(LOGIN_URL_PREFIX "/changepass/%s",
										info->name));
					w_println(wdesc,
							  "<table><tr><td>Old Password</td><td><input maxLength=\"50\" size=\"35\" name=\"oldpwd\"></td></tr>");
					w_println(wdesc,
							  "<tr><td>New Password</td><td><input maxLength=\"50\" size=\"35\" name=\"newpwd\"></td></tr>");
					w_println(wdesc,
							  "<tr><td>Confirm Password</td><td><input maxLenght=\"50\" size=\"35\" name=\"confirmpwd\"></td></tr>");
					w_println(wdesc,
							  "<tr><td><input type=\"submit\" value=\"Submit\" />");
					w_println(wdesc,
							  "</td><td><input type=\"reset\" value=\"Clear Form\" />");
					w_println(wdesc, "</td></tr></table>");
					w_printlnf(wdesc,
							   "<input type=\"hidden\" name=\"player\" value=\"%s\">",
							   info->name);
					w_println(wdesc, "</form>");
					print_footer(wdesc);
					return true;
				}

				opass = substr(wdesc->buf, "oldpwd=", "&");
				npass = substr(wdesc->buf, "newpwd=", "&");
				cpass = substr(wdesc->buf, "confirmpwd=", "&");
				uname = substr(wdesc->buf, "player=", "&");

				if (NullStr(opass) || NullStr(npass) || NullStr(cpass)
					|| NullStr(uname))
				{
					http_status(wdesc, 500);
					send_content(wdesc, CONTENT_HTML);
					w_println(wdesc, "Internal Server Error.");
					return true;
				}

				for (wp = wpwd_first; wp; wp = wp->next)
				{
					if (!str_cmp(uname, wp->name))
					{
						Base64Decode(opass, (unsigned char *) wp->name, MIL);
						Base64Decode(npass, (unsigned char *) wp->name, MIL);
						Base64Decode(cpass, (unsigned char *) wp->name, MIL);
						meta_refresh = 10;
						meta_location =
							HTTP_URL(get_prev(wdesc->path, wdesc->baseurl));
						if (info->level < MAX_LEVEL
							&& str_casecmp(wp->passw, crypt(opass, wp->name)))
						{
							print_header(wdesc, "Old password doesn't match");
							w_println(wdesc,
									  "The old password provided does not match.");
							print_footer(wdesc);
							return true;
						}
						if (str_casecmp(npass, cpass))
						{
							print_header(wdesc, "New passwords don't match");
							w_println(wdesc,
									  "The new password does not match the confirmed password.");
							print_footer(wdesc);
							return true;
						}
						print_header(wdesc, "Password changed");
						w_printlnf(wdesc, "<p>Password changed for '%s'.",
								   wp->name);
						print_footer(wdesc);
						return true;
					}
				}
			}
		}
	}
	else
	{
		print_header(wdesc, "Invalid Username/Password");
		w_printlnf(wdesc,
				   "<p>You're Username and Password should be your,"
				   " Character name on the mud, and the password you set\n"
				   " with the 'webpass' command.  Each field is Case Sensitive.</p>\n");
		print_footer(wdesc);
		return true;
	}
	return false;
}

Web_Fun(HandleHistoryRequest)
{
	int i;
	bool found = false;

	print_header(wdesc, "Channel History");
	w_println(wdesc, "<p class=\"pre\">");
	for (i = (www_index + 1) % 20; i != www_index; i = (i + 1) % 20)
	{
		if (!NullStr(www_history[i]))
		{
			found = true;
			w_printlnf(wdesc, "%s", strip_color(www_history[i]));
		}
	}
	if (!NullStr(www_history[www_index]))
	{
		w_printlnf(wdesc, "%s", strip_color(www_history[www_index]));
		found = true;
	}

	if (!found)
		w_println(wdesc, "None.");
	else
		w_printlnf(wdesc, LF "Current time: %s.",
				   str_time(-1, -1, "%I:%M:%S %p"));
	w_println(wdesc, "</p>");
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleIndexRequest)
{

	print_header(wdesc, NULL);

	w_println(wdesc,
			  "<img src=\"http://www.firstmud.com/images/firstmud_greet.jpg\" alt=\"Welcome to 1stMud\">");
	w_printlnf(wdesc, "<p>Current time is %s.</p>", str_time(-1, -1, NULL));
	w_printlnf(wdesc, "<p>%s started up at %s (%s ago).</p>", mud_info.name,
			   str_time(boot_time, -1, NULL),
			   timestr(current_time - boot_time, false));
	w_printlnf(wdesc,
			   "<p>The in game time is %d o'clock %s, Day of %s, %s the Month of %s, year %d.</p>",
			   (time_info.hour % (HOURS_IN_DAY / 2) ==
				0) ? (HOURS_IN_DAY / 2) : time_info.hour % (HOURS_IN_DAY / 2),
			   time_info.hour >= (HOURS_IN_DAY / 2) ? "pm" : "am",
			   day_name[(time_info.day + 1) % DAYS_IN_WEEK],
			   ordinal_string(time_info.day + 1), month_name[time_info.month],
			   time_info.year);

	w_printlnf(wdesc,
			   "<p>Connect to %s: <a href='telnet://%s:%d/'>telnet://%s:%d/"
			   "</a></p>", mud_info.name, HOSTNAME, mainport, HOSTNAME,
			   mainport);

	if (crs_info.timer > -1 && crs_info.status != CRS_NONE)
		w_printlnf(wdesc, "<p>%s</p>", crs_sprintf(true, false));
	else if (mud_info.last_copyover)
		w_printlnf(wdesc, "<p>Last copyover was %s ago.</p>",
				   timestr(current_time - mud_info.last_copyover, false));

	if (mud_info.longest_uptime)
		w_printlnf(wdesc, "<p>Longest uptime was %s.</p>",
				   timestr(mud_info.longest_uptime, false));

	if (war_info.status == WAR_OFF)
		w_printlnf(wdesc, "<p>The next global war starts in %s.",
				   intstr(war_info.timer, "minute"));
	else
		w_printlnf(wdesc,
				   "<p>There is %s left in the %s war for levels %d to %d.</p>",
				   intstr(war_info.timer, "minute"),
				   wartype_name(war_info.wartype, false), war_info.min_level,
				   war_info.max_level);

	if (gquest_info.running == GQUEST_OFF)
		w_printlnf(wdesc, "<p>The next global quest starts in %s.",
				   intstr(gquest_info.timer, "minute"));
	else
		w_printlnf(wdesc,
				   "<p>There is %s left in the global quest for levels %s to %d (%d targets).",
				   intstr(gquest_info.timer, "minute"), gquest_info.minlevel,
				   gquest_info.maxlevel, gquest_info.mob_count);

	if (auction_first)
	{
		AuctionData *auc, *auc_next;

		w_println(wdesc, "<p><h4>Auction - Current List of Inventory</h4>");
		w_println(wdesc,
				  "<table><th>Num</th><th>Seller</th><th>Buyer</th><th>Item Description                   Lvl</th><th>Last Bid</th><th>Time</th></tr>");

		for (auc = auction_first; auc; auc = auc_next)
		{
			auc_next = auc->next;

			if (!auc->item)
			{
				reset_auc(auc, true);
				continue;
			}

			w_printlnf(wdesc,
					   "<tr><td>%d</td><td>- %s</td><td>%s</td><td>%s</td><td>%d</td><td>%ld%s</td><td>%s</td></tr>",
					   auc->number, GetName(auc->owner),
					   auc->high_bidder ? GetName(auc->high_bidder) : "None",
					   auc->number, html_colorconv(auc->item->short_descr),
					   auc->item->level, auc->bid, auc_type(auc->type, true),
					   format_pulse(auc->status));
		}
		w_println(wdesc, "</table></p>");
	}
	w_printlnf(wdesc,
			   "<p>Visit the " MUDSTRING " Homepage at "
			   "<a href=\"http://www.firstmud.com/\">http://www.firstmud.com/</a>.</p>");
	w_printlnf(wdesc,
			   "<p>%s Admin email: <a href=\"mailto:%s@%s?%s Homepage\">%s@%s</a></p>",
			   mud_info.name, UNAME, HOSTNAME, mud_info.name, UNAME,
			   HOSTNAME);
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleUnknownRequest)
{
	http_error_code = 400;
	print_header(wdesc, "Error");
	w_printlnf(wdesc, "<p>Unknown url '%s'.</p>", wdesc->path);
	w_printf(wdesc,
			 "<p>For a list of url's this web server handles see the <a href=\"%s\">Index</a>.</p>\n",
			 HTTP_URL(NULL));
	print_footer(wdesc);
	return true;
}

Web_Fun(HandleNameGenRequest)
{
	NameProfile *nl;
	int count;

	if (!name_profile_first)
	{
		print_header(wdesc, "Name Generator");
		w_println(wdesc,
				  "<p>Random name generation is currently unavailable.</p>");
		print_footer(wdesc);
		return true;
	}

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Name Generator");
		w_println(wdesc, "<p>Pick a name profile:</p><ul>");
		for (count = 0, nl = name_profile_first; nl; nl = nl->next, count++)
		{
			w_printlnf(wdesc, "<li><a href=\"%s\">%s</a>",
					   HTTP_URL(NAMEGEN_URL_PREFIX "/%d", count), nl->title);
		}
		w_printlnf(wdesc, "</ul>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (count = 0, nl = name_profile_first; nl; nl = nl->next, count++)
		{
			if (!str_cmp(FORMATF("%d", count), wdesc->buf))
			{
				int i;

				print_header(wdesc,
							 FORMATF("%s Names", capitalize(nl->title)));
				w_println(wdesc, "<table><tr><td><ul>");
				for (i = 0; i < 20; i++)
					w_printlnf(wdesc, "<li>%s</li>", capitalize(genname(nl)));
				w_println(wdesc, "</ul></td><td><ul>");
				for (i = 0; i < 20; i++)
					w_printlnf(wdesc, "<li>%s</li>", capitalize(genname(nl)));
				w_println(wdesc, "</ul></td><td><ul>");
				for (i = 0; i < 20; i++)
					w_printlnf(wdesc, "<li>%s</li>", capitalize(genname(nl)));
				w_println(wdesc, "</ul></td></tr></table>");
				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

Web_Fun(HandleLyricsRequest)
{
	int i;

	if (NullStr(wdesc->buf))
	{
		print_header(wdesc, "Lyrics");
		w_println(wdesc, "<p>Please choose a song:</p><ul>");
		for (i = 0; i < top_song; i++)
		{
			w_printlnf(wdesc, "<li><a href=\"%s\">%s</a> by %s</li>",
					   HTTP_URL(SONG_URL_PREFIX "/%d", i), song_table[i].name,
					   song_table[i].group);
		}
		w_println(wdesc, "</ul>");
		print_footer(wdesc);
		return true;
	}
	else
	{
		for (i = 0; i < top_song; i++)
		{
			if (!str_cmp(FORMATF("%d", i), wdesc->buf))
			{
				int j;

				print_header(wdesc,
							 FORMATF("Lyrics for %s by %s",
									 song_table[i].name,
									 song_table[i].group));
				w_printlnf(wdesc, "<p class=\"pre\">");
				for (j = 0; j < song_table[i].lines; j++)
					w_println(wdesc, song_table[i].lyrics[j]);
				w_println(wdesc, "</p>");
				print_footer(wdesc);
				return true;
			}
		}
	}
	return false;
}

#ifndef DISABLE_I3
Web_Fun(HandleI3Request)
{
	I3_MUD *mud;
	int mudcount;

	print_header(wdesc, "InterMUD3");
	if (i3mud_first == NULL)
	{
		w_println(wdesc, "<p>There are no muds to list!?</p>");
		print_footer(wdesc);
		return true;
	}
	w_println(wdesc,
			  "<table><tr><th>Name</th><th>Type</th><th>Mudlib</th><th>Address</th><th>Port</th></tr>");
	for (mudcount = 0, mud = i3mud_first; mud; mud = mud->next)
	{
		if (mud == NULL)
		{
			bug("I3_mudlist: NULL mud found in listing!");
			continue;
		}

		if (mud->name == NULL)
		{
			bug("I3_mudlist: NULL mud name found in listing!");
			continue;
		}

		if (mud->status == 0)
			continue;

		mudcount++;

		switch (mud->status)
		{
			case -1:
				if (NullStr(mud->web))
					w_printlnf(wdesc,
							   "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%d</td></tr>",
							   mud->name, mud->mud_type, mud->mudlib,
							   mud->ipaddress, mud->player_port);
				else
					w_printlnf(wdesc,
							   "<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%d</td></tr>",
							   mud->web, mud->name, mud->mud_type,
							   mud->mudlib, mud->ipaddress, mud->player_port);
				break;
			case 0:
				w_printlnf(wdesc, "<tr><td colspan=\"5\">%s (down)</td></tr>",
						   mud->name);
				break;
			default:
				w_printlnf(wdesc,
						   "<tr><td colspan=\"5\">%s (rebooting, back in %d seconds)</td></tr>",
						   mud->name, mud->status);
				break;
		}
	}
	w_printlnf(wdesc,
			   "<tr><td colspan=\"5\"><hr>%d total muds listed.</td></tr></table>",
			   mudcount);
	print_footer(wdesc);
	return true;
}
#endif

bool web_is_connected(void)
{
	return (web_control != INVALID_SOCKET);
}

void update_web_server(void)
{
	SOCKET max_fd;
	WebDescriptor *current, *next;
	fd_set readfds;
	static struct timeval ZERO_TIME = {
		0, 0
	};

	const char ENDREQUEST[5] = {
		13, 10, 13, 10, 0
	}
	;

	if (!web_is_connected())
		return;

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

	max_fd = web_control;

	for (current = webdesc_first; current; current = current->next)
	{
		FD_SET(current->fd, &readfds);
		if (max_fd < current->fd)
			max_fd = current->fd;
	}

	select(max_fd + 1, &readfds, NULL, NULL, &ZERO_TIME);

	if (FD_ISSET(web_control, &readfds))
	{

		socklen_t size;
		struct sockaddr_in sock;

		current = new_webdesc();
		size = sizeof(sock);

		current->request[0] = '\0';

		if ((current->fd = accept(web_control, (SOCKADDR *) & sock, &size)) ==
			INVALID_SOCKET)
		{
			socket_error("web-accept");
			free_webdesc(current);
			FD_CLR(web_control, &readfds);
			return;
		}

		current->their_addr = ntohl(sock.sin_addr.s_addr);

		Link(current, webdesc, next, prev);

	}

	for (current = webdesc_first; current; current = current->next)
	{
		if (FD_ISSET(current->fd, &readfds))
		{
			char buf[MIL];
			int numbytes;

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

			buf[numbytes] = '\0';

			strcat(current->request, buf);
		}
	}

	for (current = webdesc_first; current; current = next)
	{
		next = current->next;

		if (strstr(current->request, "HTTP/1.")
			&& strstr(current->request, ENDREQUEST))
			handle_web_request(current);
		else if (!strstr(current->request, "HTTP/1.")
				 && strchr(current->request, '\n'))
			handle_web_request(current);
		else
		{
			continue;
		}

		closesocket(current->fd);
		UnLink(current, webdesc, next, prev);
		free_webdesc(current);
	}

}

const struct request_type request_table[] = {
	{"unknown", NULL, HandleUnknownRequest, false, false},
	{"index", "Home", HandleIndexRequest, false, false},
	{"online", "Online", HandleWhoRequest, false, false},
	{"tech", "Tech", HandleMemoryRequest, false, false},
	{"areas", "Areas", HandleAreaRequest, false, false},
	{"spells", "Spells", HandleSpellsRequest, false, false},
	{"rules", "Rules", HandleRulesRequest, false, false},
	{"credits", "Credits", HandleCreditsRequest, false, false},
	{"socials", "Socials", HandleSocialsRequest, false, false},
	{HELP_URL_PREFIX, "Helps", HandleHelpsRequest, false, true},
	{NOTE_URL_PREFIX, "Notes", HandleNotesRequest, false, true},
	{RACE_URL_PREFIX, "Races", HandleRaceRequest, false, false},
	{DEITY_URL_PREFIX, "Deities", HandleDeityRequest, false, false},
	{CMD_URL_PREFIX, "Commands", HandleCommandsRequest, false, true},
	{CLAN_URL_PREFIX, "Clans", HandleClanRequest, false, false},
	{CLASS_URL_PREFIX, "Classes", HandleClassRequest, false, true},
	{"history", "History", HandleHistoryRequest, false, false},
	{STAT_URL_PREFIX, "Stats", HandleStatsRequest, false, true},
	{CHANGE_URL_PREFIX, "Changes", HandleChangesRequest, false, true},
	{ROOM_URL_PREFIX, "Explore", HandleRoomsRequest, false, true},
	{NAMEGEN_URL_PREFIX, "Name Generator", HandleNameGenRequest, false, true},
	{SONG_URL_PREFIX, "Song Lyrics", HandleLyricsRequest, false, true},
#ifndef DISABLE_I3
	{"i3", "InterMUD3", HandleI3Request, false, true},
#endif
	{OBJ_URL_PREFIX, NULL, HandleObjsRequest, false, true},
	{CHAR_URL_PREFIX, NULL, HandleMobsRequest, false, true},
	{ADMIN_URL_PREFIX, "Admin", HandleImmRequest, true, true},
	{LOGIN_URL_PREFIX, "Login", HandlePlayersRequest, true, true},
	{NULL, NULL, NULL, false, false}
};

void format_req(WebDescriptor * wdesc)
{
	char temp[MSL];

	wdesc->stuff = str_dup(first_arg(wdesc->request, temp, false));
	first_arg(wdesc->stuff, temp, false);

	if (temp[0] == '/')
		wdesc->path = str_dup(&temp[1]);
	else
		wdesc->path = str_dup(temp);

	wdesc->buf = get_next(wdesc->path, wdesc->baseurl);

	return;
}

void handle_web_request(WebDescriptor * wdesc)
{
	int i;

	strcpy(crash_info.logline, "handle_web_request");
	if (!strstr(wdesc->request, "GET"))
	{
		http_status(wdesc, 501);
		send_content(wdesc, CONTENT_HTML);
		w_println(wdesc, "Not Implemented");
		return;
	}

	format_req(wdesc);

	if (NullStr(wdesc->path))
	{
		HandleIndexRequest(wdesc);
		mud_info.stats.web_requests++;
		return;
	}

	for (i = 0; request_table[i].fun != NULL; i++)
	{
		if (request_table[i].secure && !strstr(wdesc->request, "HTTP/1."))
			break;

		if (!str_prefix(request_table[i].req, wdesc->path))
		{
			if ((*request_table[i].fun) (wdesc))
			{
				logf("WebServer: '%s' requested.", wdesc->path);
				mud_info.stats.web_requests++;
				return;
			}
			else
				break;
		}
	}

	logf("WebServer: unknown request '%s'.", wdesc->path);
	HandleUnknownRequest(wdesc);
	strcpy(crash_info.logline, "N/A");
}

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

	if (!web_is_connected())
		return;

#ifdef WIN32

	if (crs_info.status == CRS_COPYOVER)
		return;
#endif

	for (current = webdesc_first; current; current = next)
	{
		next = current->next;
		closesocket(current->fd);
		UnLink(current, webdesc, next, prev);
		free_webdesc(current);
	}

	log_string("Closing webserver...");
	closesocket(web_control);
	web_control = INVALID_SOCKET;
}

#endif