1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  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
{
  int at;
  char *norm;
  char *bryte;
}
htmlcolor_table[] =
{
  {
  0, "black", "gray"}
  ,
  {
  1, "maroon", "red"}
  ,
  {
  2, "green", "lime"}
  ,
  {
  3, "orange", "yellow"}
  ,
  {
  4, "navy", "blue"}
  ,
  {
  5, "magenta", "purple"}
  ,
  {
  6, "teal", "cyan"}
  ,
  {
  7, "silver", "white"}
  ,
  {
  -1, NULL, NULL}
};

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

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

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

  convert_random (a);

  b = (a->at[CT_ATTR] == CL_BRIGHT);

  if (VALID_BG (a->at[CT_BACK]))
    {
      if ((col = htmlcolor_lookup (a->at[CT_BACK] - 40, b)) == NULL)
	{
	  script = true;
	  col = "' + document.bgColor + '";
	}
      sprintf (buf, " background-color: %s;", col);

    }

  if (VALID_FG (a->at[CT_FORE]))
    {
      if ((col = htmlcolor_lookup (a->at[CT_FORE] - 30, b)) == NULL)
	{
	  script = true;
	  col = "' + document.fgColor + '";
	}

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

  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;

  c.at[CT_ATTR] = CL_NONE;
  c.at[CT_FORE] = FG_NONE;
  c.at[CT_BACK] = BG_NONE;

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

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



	      if (VALID_CL (slot))
		{
		  c.at[CT_ATTR] = slot + CL_MOD;
		}

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

	  if (*point == '=')
	    {
	      point++;
	      c.at[CT_BACK] = c.at[CT_FORE] + CL_MOD;
	    }

	  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 (current_time, -1, fmt));
		}
	      else
		strcpy (buf, str_time (current_time, -1, NULL));
	      break;
	    case 'P':
	    case 'p':
	      break;
	    case COLORCODE:
	      sprintf (buf, "%c", COLORCODE);
	      break;
	    case 'X':
	    case 'x':
	      c.at[CT_ATTR] = CL_NONE;
	      c.at[CT_FORE] = FG_NONE;
	      c.at[CT_BACK] = BG_NONE;
	      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 = 0, pos;

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

	  if (slot < 0 || slot >= MAX_CUSTOM_COLOR
	      || (pos = color_slot_lookup (slot)) == -1)
	    {
	      bug ("invalid custom color");
	      strcpy (buf, " ");
	    }
	  else
	    {
	      copy_array (c.at, color_table[pos].col_attr, CT_MAX);
	      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"

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", Upper (title), mud_info.name);
  else
    w_printlnf (wdesc, "%s Homepage", mud_info.name);
  w_println (wdesc, "</title>");
  w_println (wdesc,
	     "<style type=\"text/css\" title=\"websrv.css\" media=\"screen\">");
  w_println (wdesc,
	     "* { background-color: black; color: silver; font: small sans-serif; letter-spacing: 1px;}");
  w_println (wdesc, "a { text-decoration: none; color: #FFFFCC; }");
  w_println (wdesc,
	     "a:hover { text-decoration: underline; cursor: crosshair; color: #FFFF66;}");
  w_println (wdesc,
	     "hr { color: maroon; }  .header { font-size: larger; letter-spacing: 7px; ");
  w_println (wdesc,
	     "text-decoration: underline; text-indent: 50px; } .above { color: gray; ");
  w_println (wdesc,
	     "font-size: xx-large; text-indent: 50px; letter-spacing: 7px; }");
  w_println (wdesc,
	     ".small { font-size: x-small; }  .footer { font-size: x-small; padding-top: 1cm; position: relative; }");
  w_println (wdesc,
	     ".main { position: relative; left: 140px; top: 20px; width: 80%; } ");
  w_println (wdesc, ".main * td { padding: 4px 4px 4px 4px; }");
  w_println (wdesc,
	     ".menu { border: 1px solid #333333; position: absolute; top: 20px; width: 130px; }");
  w_println (wdesc,
	     ".top { border: 1px solid; font-size: x-large; padding: 4px 4px 4px 4px; }");
  w_println (wdesc, "img { border: none; } ul { list-style-type: square; } ");
  w_println (wdesc,
	     "th { text-align: left; text-indent: 10px; text-decoration: underline; letter-spacing: 5px; }");
  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=\"menu\"><table><tr><th>Contents</th></tr>");
  for (i = 0; request_table[i].req != NULL; i++)
    {
      if (!request_table[i].name)
	continue;

      w_printlnf (wdesc, "<tr><td><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,
	     "<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></td></tr><tr><td><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></td></tr>");
  w_println (wdesc,
	     "<tr><td><a class=\"small\" href=\"http://www.mudconnect.com/\">mudconnect.com</a></td></tr>");
  w_println (wdesc,
	     "<tr><td><a class=\"small\" href=\"javascript:void(0);\" onclick=\"window.open('http://www.topmudsites.com/cgi-bin/topmuds/rankem.cgi?id=markanth');\">");
  w_println (wdesc, "topmudsites.com</a></td></tr>");
  w_println (wdesc, "</table>");
  w_println (wdesc, "</div><div class=\"main\">");
  if (!NullStr (title))
    w_printlnf (wdesc, "<div class=\"top\">%s @ %s</div>",
		Upper (title), mud_info.name);
  else
    w_printlnf (wdesc, "<div class=\"top\">%s Homepage</div>", mud_info.name);

}

void
print_footer (WebDescriptor * wdesc)
{
  w_println (wdesc, "<div class=\"footer\">");
  w_println (wdesc, "<br><address>");
  w_printlnf (wdesc,
	      "This page is automatically generated by %s (%s).",
	      mud_info.name, str_time (-1, -1, NULL));
  w_println (wdesc, "</address>");
  w_println (wdesc,
	     "<table width=\"55%\"><tr><td><hr><table><tr><td></td><td><table><tr><td><b>1stMud Server Project Ring </b></td></tr><tr><td> [ <a href=\"http://C.webring.com/wrman?ring=1stmudserverproj;addsite\">Join</a> | <a href=\"http://C.webring.com/hub?ring=1stmudserverproj;id=3;hub\">List</a> | <a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;random\">Random</a> | <b><a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;prev\">&lt;&lt; Prev</a> | <a href=\"http://C.webring.com/go?ring=1stmudserverproj;id=3;next\">Next &gt;&gt;</a></b> ] </td></tr></table></td></tr></table></td></tr></table>");
  w_println (wdesc, "</div></div></body>");
  w_println (wdesc, "</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_KILLER))
	strcat (buf, "(KILLER) ");
      if (!IsNPC (victim) && IsSet (victim->act, PLR_THIEF))
	strcat (buf, "(THIEF) ");
      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>");
      w_println (wdesc, 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;
}

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;
	  int door;

	  print_header (wdesc, pRoom->name);
	  w_println (wdesc, "<table><tr><td colspan=3>");
	  w_println (wdesc, html_colorconv (pRoom->name));
	  w_printlnf (wdesc, "</td><td colspan=3>%s", pRoom->area->name);
	  w_println (wdesc, "</td></tr><tr><td colspan=6>");
	  w_println (wdesc, html_colorconv (pRoom->description));
	  w_println (wdesc, "</td></tr><tr>");
	  for (door = 0; door < MAX_DIR; door++)
	    {
	      if ((pexit = pRoom->exit[door]) != NULL
		  && pexit->u1.to_room != NULL)
		{
		  w_printlnf (wdesc,
			      "<td><a href='%s'>%s</a></td>",
			      HTTP_URL (ROOM_URL_PREFIX "/%ld",
					pexit->u1.to_room->vnum),
			      dir_name[door]);
		}
	      else
		w_println (wdesc, "<td></td>");
	    }
	  w_printlnf (wdesc, "</tr><tr><td colspan=6>%s</td>",
		      show_list_to_html (pRoom->content_first));
	  w_printlnf (wdesc, "</tr><tr><td colspan=6>%s</td>",
		      show_char_to_html (pRoom->person_first));

	  w_println (wdesc, "</tr></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 (current_time, -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 (current_time, -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},
  {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;



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

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