/* ROM 2.4 Integrated Web Server - Version 1.0 * * This is my first major snippet... Please be kind. ;-) * Copyright 1998 -- Defiant -- Rob Siemborski -- mud@towers.crusoe.net * * Many thanks to Russ and the rest of the developers of ROM for creating * such an excellent codebase to program on. * * If you use this code on your mud, I simply ask that you place my name * someplace in the credits. You can put it where you feel it is * appropriate. * * I offer no guarantee that this will work on any mud except my own, and * if you can't get it to work, please don't bother me. I wrote and tested * this only on a Linux 2.0.30 system. Comments about bugs, are, however, * appreciated. * * Now... On to the installation! */ /* * Insanity v0.9a pre-release Modifications * By Chris Fewtrell (Trax) <C.J.Fewtrell@bcs.org.uk> * * - Added functionailiy for Secure Web server pages, using standard HTTP * Basic authentication, comparing with pass list generated with command * from within the MUD itself. * - Started work on web interface to help files, allowing them to be browsed * from a web browser rather than being in MUD to read them. * - Seperated out the HTTP codes and content type to seperate functions * (intending to allow more than HTML to be served via this) * - Adjusted the descriptor handling to prevent anyone from prematurely * stopping a transfer causing a fd exception and the system to exit() * - Created a sorta "virtual" web directory for the webserver files to be * actually served. This contains the usual images dir if any images are * needed to be served from a central repository rather than generated. * Be warned though! It WON'T follow any symlinks, I'll add that later * with the stat function.. (maybe :) * - Including a MUD-Net web module to add the functionaility of the MUD-Net webserver * code directly into the mud itself for use here, preventing the need for * the seperate server and client processes (I know it sorta depends on the * mud be FE never stays down for long..) * * Future Possbile additions: * - Access to general boards though web interface, prolly prevent posting but * being able to browse and read notes to 'all' would be allowed */ /* * Additional Modifications based upon with with Insanity Codebase * By Chris Fewtrell (Trax) <C.J.Fewtrell@bcs.org.uk> * * - Web server root directory created, all URLs that use internal code * generated HTML intercept first, then path is handed off to webroot * handler allowing a directory tree to be built up there. Should allow * easier management of the file behind the internal web server. */ #include <errno.h> #include <stdarg.h> #include <sys/types.h> #if !defined(WIN32) #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <sys/time.h> #include <sys/stat.h> #include <dirent.h> #endif #include <fcntl.h> #include <ctype.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include "merc.h" #include "webserver.h" #include "globals.h" #include "interp.h" #include "tables.h" #include "olc.h" #include "lookup.h" #include "recycle.h" #include "magic.h" #include "gsn.h" #include "telnet.h" void update_webpasses(CHAR_DATA * ch, bool pDelete) { WPWD_DATA *c_next; WPWD_DATA *curr; if (IS_NPC(ch)) return; for (curr = wpwd_first; curr != NULL; curr = c_next) { c_next = curr->next; if (!str_cmp(ch->name, curr->name)) { UNLINK(curr, wpwd_first, wpwd_last, next, prev); free_pwd(curr); save_webpasses(); } } if (pDelete || IS_NULLSTR(ch->pcdata->webpass)) return; curr = new_pwd(); replace_string(curr->name, ch->name); replace_string(curr->passw, ch->pcdata->webpass); LINK(curr, wpwd_first, wpwd_last, next, prev); save_webpasses(); return; } CH_CMD(do_webpass) { char arg1[MIL]; char *pArg; char *pwdnew; char *p; char cEnd; if (!ch || IS_NPC(ch)) return; if (!IS_IMMORTAL(ch)) { chprintln(ch, "This feature is only available to immortals, sorry."); return; } pArg = arg1; while (isspace(*argument)) argument++; cEnd = ' '; if (*argument == '\'' || *argument == '\"') cEnd = *argument++; while (*argument != '\0') { if (*argument == cEnd) { argument++; break; } *pArg++ = *argument++; } *pArg = '\0'; if (IS_NULLSTR(arg1)) { chprintln(ch, "Syntax: webpass <new>."); return; } write_to_descriptor(ch->desc, echo_off_str, 0); if (strlen(arg1) < 5) { chprintln(ch, "New password must be at least five characters long."); write_to_descriptor(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."); write_to_descriptor(ch->desc, echo_on_str, 0); return; } } replace_string(ch->pcdata->webpass, pwdnew); save_char_obj(ch); update_webpasses(ch, FALSE); chprintln(ch, "Ok."); write_to_descriptor(ch->desc, echo_on_str, 0); return; } bool check_web_pass(const char *username, const char *password) { WPWD_DATA *current; for (current = wpwd_first; current; current = current->next) if (!strcasecmp(current->name, username)) if (!strcasecmp(current->passw, crypt(password, username))) return TRUE; return FALSE; } /* Thanks to John Ludeman for this code... * Define translation matrix for Base64 decode. * it's fast and const should make it shared text page. */ const int pr2six[256] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 }; bool WebUP = FALSE; /* * Decode a base64 encoded string */ void Base64Decode(char *bufcoded, unsigned char *bufplain, int outbufsize) { int nbytesdecoded; int nprbytes; char *bufin = bufcoded; unsigned char *bufout = bufplain; /* Strip leading whitespace */ while (*bufcoded == ' ' || *bufcoded == '\t') ++bufcoded; /* * Figure out how many characters are in the input buffer. * If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */ bufin = bufcoded; while (pr2six[(int) *(bufin++)] <= 63) ; nprbytes = bufin - bufcoded - 1; nbytesdecoded = ((nprbytes + 3) / 4) * 3; if (nbytesdecoded > outbufsize) nprbytes = (outbufsize * 4) / 3; bufin = bufcoded; while (nprbytes > 0) { *(bufout++) = (unsigned char) (pr2six[(int) *bufin] << 2 | pr2six[(int) bufin[1]] >> 4); *(bufout++) = (unsigned char) (pr2six[(int) bufin[1]] << 4 | pr2six[(int) bufin[2]] >> 2); *(bufout++) = (unsigned char) (pr2six[(int) bufin[2]] << 6 | pr2six[(int) bufin[3]]); bufin += 4; nprbytes -= 4; } if (nprbytes & 03) { if (pr2six[(int) bufin[-2]] > 63) nbytesdecoded -= 2; else nbytesdecoded -= 1; } bufplain[nbytesdecoded] = '\0'; } /* The mark of the end of a HTTP/1.x request */ const char ENDREQUEST[5] = { 13, 10, 13, 10, 0 }; /* (CRLFCRLF) */ /* Locals */ WEB_DESCRIPTOR *first_webdesc; WEB_DESCRIPTOR *last_webdesc; int web_socket; bool init_web_server() { struct sockaddr_in my_addr; int x = 1; first_webdesc = NULL; last_webdesc = NULL; logf("Attaching Internal Web Server to Port %d", WEBSERVERPORT); if ((web_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { log_string("----> Web Server: Error getting socket"); perror("web-socket"); return FALSE; } if (setsockopt(web_socket, SOL_SOCKET, SO_REUSEADDR, (void *) &x, sizeof(x)) < 0) { perror("init_web: SO_REUSEADDR"); close(web_socket); web_socket = -1; return FALSE; } #if defined(SO_LINGER) && !defined(SYSV) { struct linger ld; ld.l_onoff = 1; ld.l_linger = 1000; if (setsockopt (web_socket, SOL_SOCKET, SO_LINGER, (void *) &ld, sizeof(ld)) < 0) { perror("Init_web: SO_LINGER"); close(web_socket); web_socket = -1; return FALSE; } } #endif memset(&my_addr, '\0', sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(WEBSERVERPORT); if ((bind(web_socket, (struct sockaddr *) &my_addr, sizeof(my_addr))) == -1) { log_string("----> Web Server: Error binding socket"); perror("web-bind"); close(web_socket); web_socket = -1; return FALSE; } /* Only listen for 5 connects at once, do we really need more? */ /* We might now since I've attached this to the live web page - Samson */ if (listen(web_socket, 20) < 0) { perror("web-listen"); close(web_socket); web_socket = -1; return FALSE; } return TRUE; } struct timeval ZERO_TIME = { 0, 0 }; void update_web_server(void) { int max_fd; WEB_DESCRIPTOR *current, *next; fd_set readfds; FD_ZERO(&readfds); FD_SET(web_socket, &readfds); /* it *will* be atleast web_socket */ max_fd = web_socket; /* add in all the current web descriptors */ for (current = first_webdesc; current; current = current->next) { FD_SET(current->fd, &readfds); if (max_fd < current->fd) max_fd = current->fd; } /* Wait for ONE descriptor to have activity */ select(max_fd + 1, &readfds, NULL, NULL, &ZERO_TIME); if (FD_ISSET(web_socket, &readfds)) { /* NEW CONNECTION -- INIT & ADD TO LIST */ alloc_mem(current, WEB_DESCRIPTOR, 1); current->sin_size = sizeof(struct sockaddr_in); current->request[0] = '\0'; if ((current->fd = accept(web_socket, (struct sockaddr *) &(current->their_addr), &(current->sin_size))) == -1) { log_string("----> Web Server: Error accepting connection"); perror("web-accept"); free_mem(current); FD_CLR(web_socket, &readfds); return; } LINK(current, first_webdesc, last_webdesc, next, prev); /* END ADDING NEW DESC */ } /* DATA IN! */ for (current = first_webdesc; current; current = current->next) { if (FD_ISSET(current->fd, &readfds)) /* We Got Data! */ { char buf[MAXDATA]; int numbytes; if ((numbytes = read(current->fd, buf, sizeof(buf))) == -1) { perror("web-read"); continue; } buf[numbytes] = '\0'; strcat(current->request, buf); } } /* DONE WITH DATA IN */ /* DATA OUT */ /* Hmm we want to delay this if possible, to prevent it prematurely */ for (current = first_webdesc; current; current = next) { next = current->next; if (strstr(current->request, "HTTP/1.") /* 1.x request (vernum on FIRST LINE) */ && strstr(current->request, ENDREQUEST)) handle_web_request(current); else if (!strstr(current->request, "HTTP/1.") && strchr(current->request, '\n')) /* HTTP/0.9 (no ver number) */ handle_web_request(current); else { continue; /* Don't have full request yet! */ } close(current->fd); UNLINK(current, first_webdesc, last_webdesc, next, prev); free_mem(current); } /* END DATA-OUT */ } /* Generic Utility Function */ int send_buf(int fd, const char *fmt, ...) { char buf[2 * MSL]; va_list args; int len; va_start(args, fmt); len = vsnprintf(buf, 2 * MSL, fmt, args); va_end(args); if (len > 0) return send(fd, buf, strlen(buf), 0); else return len; } void send_401UNAUTHORISED(WEB_DESCRIPTOR * wdesc, char *realm) { send_buf(wdesc->fd, "HTTP/1.1 401 Unauthorised\n"); send_buf(wdesc->fd, "WWW-Authenticate: Basic realm=\"%s\"\n", realm); } bool lastcolor = FALSE; #define SET_WWW_FONT(buf, color) \ do \ { \ strcpy(buf, "<FONT color=\"" color "\">"); \ lastcolor = TRUE; \ } \ while (0); int html_colour(char type, char *string) { char code[MIL]; char out[MSL]; char *p = '\0'; if (lastcolor == TRUE) strncpy(out, "</FONT>", MSL); else out[0] = '\0'; switch (type) { case '\0': break; case ' ': strncpy(code, " ", MIL); break; default: case 'x': strncpy(code, "<script type='text/javascript'>document.writeln('<FONT color=' + document.fgColor + '>');</script>", MIL); break; case 'b': SET_WWW_FONT(code, "navy"); break; case 'c': SET_WWW_FONT(code, "teal"); break; case 'g': SET_WWW_FONT(code, "green"); break; case 'm': SET_WWW_FONT(code, "purple"); break; case 'r': SET_WWW_FONT(code, "maroon"); break; case 'w': SET_WWW_FONT(code, "silver"); break; case 'y': SET_WWW_FONT(code, "olive"); break; case 'B': SET_WWW_FONT(code, "blue"); break; case 'C': SET_WWW_FONT(code, "cyan"); break; case 'G': SET_WWW_FONT(code, "lime"); break; case 'M': SET_WWW_FONT(code, "magenta"); break; case 'R': SET_WWW_FONT(code, "red"); break; case 'W': SET_WWW_FONT(code, "white"); break; case 'Y': SET_WWW_FONT(code, "yellow"); break; case 'd': SET_WWW_FONT(code, "black"); break; case 'D': SET_WWW_FONT(code, "gray"); break; case '-': strncpy(code, "~", MIL); break; case '`': switch (number_range(1, 14)) { case 1: SET_WWW_FONT(code, "navy"); break; case 2: SET_WWW_FONT(code, "teal"); break; case 3: SET_WWW_FONT(code, "green"); break; case 4: SET_WWW_FONT(code, "purple"); break; case 5: SET_WWW_FONT(code, "maroon"); break; default: case 6: SET_WWW_FONT(code, "silver"); break; case 7: SET_WWW_FONT(code, "olive"); break; case 8: SET_WWW_FONT(code, "blue"); break; case 9: SET_WWW_FONT(code, "cyan"); break; case 10: SET_WWW_FONT(code, "lime"); break; case 11: SET_WWW_FONT(code, "magenta"); break; case 12: SET_WWW_FONT(code, "red"); break; case 13: SET_WWW_FONT(code, "white"); break; case 14: SET_WWW_FONT(code, "yellow"); break; } break; case ANSI_KEY: strncpy(code, "{", MIL); break; } strcat(out, code); p = out; while (*p != '\0') { *string = *p++; *++string = '\0'; } return (strlen(out)); } void html_colourconv(char *buffer, const char *txt) { const char *point; int skip = 0; const char *end = "</FONT>"; lastcolor = FALSE; for (point = txt; *point; point++) { if (*point == ANSI_KEY) { point++; if (*point == '\0') point--; else skip = html_colour(*point, buffer); while (skip-- > 0) ++buffer; continue; } else if (*point == ANSI_CUSTOM) { point++; while (*point != ANSI_END) point++; continue; } if (*point == '<') { *buffer = '&'; *++buffer = 'l'; *++buffer = 't'; *++buffer = ';'; *++buffer = '\0'; continue; } if (*point == '>') { *buffer = '&'; *++buffer = 'g'; *++buffer = 't'; *++buffer = ';'; *++buffer = '\0'; continue; } if (*point == '"') { *buffer = '&'; *++buffer = 'q'; *++buffer = 'u'; *++buffer = 'o'; *++buffer = 't'; *++buffer = ';'; *++buffer = '\0'; continue; } if (*point == '&') { *buffer = '&'; *++buffer = 'a'; *++buffer = 'm'; *++buffer = 'p'; *++buffer = '\0'; continue; } if (*point == '\n') { *buffer = '<'; *++buffer = 'b'; *++buffer = 'r'; *++buffer = '>'; *++buffer = '\0'; } *buffer = *point; *++buffer = '\0'; } if (lastcolor == TRUE) { for (point = end; *point; point++) { *buffer = *point; *++buffer = '\0'; } } *buffer = '\0'; return; } const char *DEFAULT_URL_PORT(void) { static char result[100]; int len; sprintf(result, "%s", DEFAULT_URL); len = strlen(result); if (result[len - 1] == '/') sprintf(result + (len - 1), ":%d/", WEBSERVERPORT); // get rid of appending backslash else sprintf(result + len, ":%d/", WEBSERVERPORT); return result; } void under_line(char *under_lined, const char *spaced_out) { char *point; strcpy(under_lined, spaced_out); for (point = under_lined; *point; point++) { if (*point == ' ') { *point = '_'; } else { *point = LOWER(*point); } } return; } int min_class_level(int sn) { int min_so_far = MAX_LEVEL; int iClass; for (iClass = 0; iClass < maxClass; iClass++) { if (skill_table[sn].skill_level[iClass] < min_so_far) min_so_far = skill_table[sn].skill_level[iClass]; } return min_so_far; } void print_header(WEB_DESCRIPTOR * wdesc, const char *title) { send_buf(wdesc->fd, DOCTYPE); send_buf(wdesc->fd, "<HTML>\n"); send_buf(wdesc->fd, "<HEAD>\n"); send_buf(wdesc->fd, "<TITLE>%s @ %s</TITLE>\n", title, MUD_NAME); send_buf(wdesc->fd, "</HEAD><BODY>\n"); send_buf(wdesc->fd, "<H2>%s @ %s</H2>\n", title, MUD_NAME); } void print_footer(WEB_DESCRIPTOR * wdesc) { send_buf(wdesc->fd, "<BR><ADDRESS>\n"); send_buf(wdesc->fd, "This page is automatically generated by " MUD_NAME " Mud.<BR>\n"); send_buf(wdesc->fd, "</ADDRESS>\n"); send_buf(wdesc->fd, "</BODY>\n"); send_buf(wdesc->fd, "</HTML>\n"); } void print_file(WEB_DESCRIPTOR * wdesc, char *filename) { FILE *fp; char buf[MSL * 4]; int c; int num = 0; if ((fp = file_open(filename, "r")) != NULL) { while (!feof(fp)) { while ((buf[num] = fgetc(fp)) != EOF && buf[num] != '\n' && buf[num] != '\r' && num < (MSL * 4 - 2)) num++; c = fgetc(fp); if ((c != '\n' && c != '\r') || c == buf[num]) ungetc(c, fp); buf[num++] = '\n'; buf[num] = '\0'; send_buf(wdesc->fd, buf); num = 0; } file_close(fp); } else { logf("Failed to open file %s.", filename); file_close(fp); } } char *format_obj_to_html(OBJ_DATA * obj) { AFFECT_DATA *paf; static char output[MSL * 5]; char buf[MSL * 5]; output[0] = '\0'; if (IS_NULLSTR(obj->description)) return output; if (IS_OBJ_STAT(obj, ITEM_INVIS)) strcat(output, "{W(Invis) "); if (IS_OBJ_STAT(obj, ITEM_DARK)) strcat(output, "{D(Dark) "); if (IS_OBJ_STAT(obj, ITEM_EVIL)) strcat(output, "{R(Red Aura) "); if (IS_OBJ_STAT(obj, ITEM_BLESS)) strcat(output, "{B(Blue Aura) "); if (IS_OBJ_STAT(obj, ITEM_MAGIC)) strcat(output, "{G(Magical) "); if (IS_OBJ_STAT(obj, ITEM_GLOW)) strcat(output, "{Y(Glowing) "); if (IS_OBJ_STAT(obj, ITEM_HUM)) strcat(output, "{C(Humming) "); if (obj->item_type == ITEM_WEAPON) { for (paf = obj->first_affect; paf; paf = paf->next) { if (IS_SET(paf->bitvector, WEAPON_FLAMING)) strcat(output, "{Y(Flaming){x "); if (IS_SET(paf->bitvector, WEAPON_FROST)) strcat(output, "{C(Frost){x "); if (IS_SET(paf->bitvector, WEAPON_VAMPIRIC)) strcat(output, "{R(Vampiric){x "); if (IS_SET(paf->bitvector, WEAPON_SHOCKING)) strcat(output, "{M(Shocking){x "); if (IS_SET(paf->bitvector, WEAPON_POISON)) strcat(output, "{g(Poison){x "); if (IS_SET(paf->bitvector, WEAPON_SHARP)) strcat(output, "{W(Sharp){x "); if (IS_SET(paf->bitvector, WEAPON_VORPAL)) strcat(output, "{r(Vorpal){x "); } } if (IS_OBJ_STAT(obj, ITEM_AUCTIONED)) strcat(output, "{Y(Auctioned){x "); if (obj->condition <= 9 && obj->condition >= 0) strcat(output, "{b(Ruined) "); else if (obj->condition >= 10 && obj->condition <= 24) strcat(output, "{b(Broken) "); strcat(output, "{x"); html_colourconv(buf, output); sprintf(output, buf); if (obj->description != NULL) { char temp[MSL]; html_colourconv(buf, obj->description); sprintf(temp, "<A href=\"%sobjs/%ld\">%s</A><br>", DEFAULT_URL_PORT(), obj->pIndexData->vnum, buf); strcat(output, temp); } return output; } char *show_list_to_html(OBJ_DATA * list) { static char output[MSL * 6]; const char **prgpstrShow; int *prgnShow; char *pstrShow; OBJ_DATA *obj; int nShow; int iShow; int count; char temp[MSL]; bool found; count = 0; for (obj = list; obj != NULL; obj = obj->next_content) count++; alloc_mem(prgpstrShow, const char *, count); alloc_mem(prgnShow, int, count); nShow = 0; output[0] = '\0'; for (obj = list; obj != NULL; obj = obj->next_content) { if (obj->wear_loc == WEAR_NONE) { pstrShow = format_obj_to_html(obj); found = FALSE; for (iShow = nShow - 1; iShow >= 0; iShow--) { if (!str_cmp(prgpstrShow[iShow], pstrShow)) { prgnShow[iShow]++; found = TRUE; break; } } if (!found) { prgpstrShow[nShow] = str_dup(pstrShow); prgnShow[nShow] = 1; nShow++; } } } for (iShow = 0; iShow < nShow; iShow++) { if (IS_NULLSTR(prgpstrShow[iShow])) { free_string(prgpstrShow[iShow]); continue; } if (prgnShow[iShow] != 1) { sprintf(temp, "(%2d) ", prgnShow[iShow]); strcat(output, temp); } else strcat(output, " "); sprintf(temp, "%s<br>", prgpstrShow[iShow]); strcat(output, temp); free_string(prgpstrShow[iShow]); if (strlen(output) > 5500) { strcat(output, " (More stuff not shown)<br>"); break; } } free_mem(prgpstrShow); free_mem(prgnShow); return output; } char *show_char_to_html_0(CHAR_DATA * victim) { static char buf[MSL * 6]; char cbuf[MSL * 6]; char temp[MSL]; buf[0] = '\0'; if (IS_AFFECTED(victim, AFF_INVISIBLE)) strcat(buf, "({wInvis{x) "); if (victim->invis_level >= LEVEL_IMMORTAL) strcat(buf, "({WWizi{x) "); if (IS_AFFECTED(victim, AFF_HIDE)) strcat(buf, "({DHide{x) "); if (IS_AFFECTED(victim, AFF_CHARM)) strcat(buf, "({MCharmed{x) "); if (IS_AFFECTED(victim, AFF_PASS_DOOR)) strcat(buf, "({cTranslucent{x) "); if (IS_AFFECTED(victim, AFF_FAERIE_FIRE)) strcat(buf, "({mPink Aura{x) "); if (IS_EVIL(victim)) strcat(buf, "({RRed Aura{x) "); if (IS_GOOD(victim)) strcat(buf, "({YGolden Aura{x) "); if (IS_AFFECTED(victim, AFF_SANCTUARY)) strcat(buf, "({WWhite Aura{x) "); if (!IS_NPC(victim) && !victim->desc) strcat(buf, "({CLinkdead{x) "); if (!IS_NPC(victim)) { if (IS_SET(victim->comm, COMM_AFK)) strcat(buf, "{Y*{RAFK{Y*{x "); if (IS_SET(victim->act, PLR_WAR)) strcat(buf, "{Y({RWAR{Y){x "); if (!IS_NPC(victim) && IS_SET(victim->act, PLR_KILLER)) strcat(buf, "({rKILLER{x) "); if (!IS_NPC(victim) && IS_SET(victim->act, PLR_THIEF)) strcat(buf, "({rTHIEF{x) "); if (IS_SET(victim->comm, COMM_QUIET)) strcat(buf, "{R[{WQUIET{R]{x "); if (victim->desc && victim->desc->editor != 0) strcat(buf, "{M[{mOLC{M]{x "); if (victim->pcdata->in_progress != NULL) strcat(buf, "{g[{wNote{g]{x "); if (IS_QUESTOR(victim)) strcat(buf, "{W[{CQ{W]{x "); } if (victim->level > 0) { long vict_condition; if (victim->max_hit > 0) vict_condition = victim->hit * 100 / victim->max_hit; else vict_condition = -1; if (vict_condition < 0) strcat(buf, "{R(DEAD){x "); else if (vict_condition < 33) strcat(buf, "{R(Wounded){x "); } if (victim->position == (IS_NPC(victim) ? victim->start_pos : POS_STANDING) && !IS_NULLSTR(victim->long_descr)) { html_colourconv(cbuf, buf); strcpy(buf, cbuf); if (IS_NPC(victim)) { html_colourconv(cbuf, victim->long_descr); sprintf(temp, "<A href=\"%schars/%ld\">%s</A>", DEFAULT_URL_PORT(), victim->pIndexData->vnum, cbuf); strcat(buf, temp); } return (buf); } if (IS_NPC(victim)) { html_colourconv(cbuf, victim->short_descr); sprintf(temp, "<A href=\"%schars/%ld\">%s</A>", DEFAULT_URL_PORT(), victim->pIndexData->vnum, cbuf); strcat(buf, temp); } else { html_colourconv(cbuf, buf); strcat(buf, cbuf); strcat(buf, victim->name); } switch (victim->position) { case POS_DEAD: strcat(buf, " is DEAD!!"); break; case POS_MORTAL: strcat(buf, " is mortally wounded."); break; case POS_INCAP: strcat(buf, " is incapacitated."); break; case POS_STUNNED: strcat(buf, " is lying here stunned."); break; case POS_SLEEPING: if (victim->on != NULL) { if (IS_SET(victim->on->value[2], SLEEP_AT)) { sprintf(temp, " is sleeping at %s.", victim->on->short_descr); strcat(buf, temp); } else if (IS_SET(victim->on->value[2], SLEEP_ON)) { sprintf(temp, " is sleeping on %s.", victim->on->short_descr); strcat(buf, temp); } else { sprintf(temp, " is sleeping in %s.", victim->on->short_descr); strcat(buf, temp); } } else strcat(buf, " is sleeping here."); break; case POS_RESTING: if (victim->on != NULL) { if (IS_SET(victim->on->value[2], REST_AT)) { sprintf(temp, " is resting at %s.", victim->on->short_descr); strcat(buf, temp); } else if (IS_SET(victim->on->value[2], REST_ON)) { sprintf(temp, " is resting on %s.", victim->on->short_descr); strcat(buf, temp); } else { sprintf(temp, " is resting in %s.", victim->on->short_descr); strcat(buf, temp); } } else strcat(buf, " is resting here."); break; case POS_SITTING: if (victim->on != NULL) { if (IS_SET(victim->on->value[2], SIT_AT)) { sprintf(temp, " is sitting at %s.", victim->on->short_descr); strcat(buf, temp); } else if (IS_SET(victim->on->value[2], SIT_ON)) { sprintf(temp, " is sitting on %s.", victim->on->short_descr); strcat(buf, temp); } else { sprintf(temp, " is sitting in %s.", victim->on->short_descr); strcat(buf, temp); } } else strcat(buf, " is sitting here."); break; case POS_STANDING: if (victim->on != NULL) { if (IS_SET(victim->on->value[2], STAND_AT)) { sprintf(temp, " is standing at %s.", victim->on->short_descr); strcat(buf, temp); } else if (IS_SET(victim->on->value[2], STAND_ON)) { sprintf(temp, " is standing on %s.", victim->on->short_descr); strcat(buf, temp); } else { sprintf(temp, " is standing in %s.", victim->on->short_descr); strcat(buf, temp); } } strcat(buf, " is here."); break; case POS_FIGHTING: strcat(buf, " is here, fighting "); if (victim->fighting == NULL) strcat(buf, "thin air??"); else if (victim->in_room == victim->fighting->in_room) { strcat(buf, IS_NPC(victim) ? victim->fighting-> short_descr : victim->fighting->name); strcat(buf, "."); } else strcat(buf, "someone who left??"); break; } return (buf); } char *show_char_to_html(CHAR_DATA * list) { CHAR_DATA *rch; static char output[MSL * 10]; output[0] = '\0'; for (rch = list; rch != NULL; rch = rch->next_in_room) { if (rch->invis_level >= LEVEL_IMMORTAL) continue; strcat(output, show_char_to_html_0(rch)); strcat(output, "<br>"); } return (output); } void HandleMemoryRequest(WEB_DESCRIPTOR * wdesc) { int sn, count_spell = 0, count_skill = 0; for (sn = 0; sn < maxSkill; sn++) { if (skill_table[sn].name == NULL) break; if (skill_table[sn].spell_fun != spell_null) count_spell += 1; else count_skill += 1; } print_header(wdesc, "Technical Info"); send_buf(wdesc->fd, "<TABLE>\n"); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Affects</TD> <TD>%5d</TD></TR>\n", top_affect); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Areas</TD> <TD>%5d</TD></TR>\n", top_area); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>ExDes</TD> <TD>%5d</TD></TR>\n", top_ed); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Exits</TD> <TD>%5d</TD></TR>\n", top_exit); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Helps</TD> <TD>%5d</TD></TR>\n", top_help); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Socials</TD> <TD>%5d</TD></TR>\n", maxSocial); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Spells</TD> <TD>%5d</TD></TR>\n", count_spell); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Skills</TD> <TD>%5d</TD></TR>\n", count_skill); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Mobs</TD> <TD>%5d (%5d in use)</TD></TR>\n", top_mob_index, mobile_count); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Objs</TD> <TD>%5d</TD></TR>\n", top_obj_index); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Resets</TD> <TD>%5d</TD></TR>\n", top_reset); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Rooms</TD> <TD>%5d</TD></TR>\n", top_room); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Shops</TD> <TD>%5d</TD></TR>\n", top_shop); send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); } void HandleRulesRequest(WEB_DESCRIPTOR * wdesc) { HELP_DATA *pHelp; print_header(wdesc, "Rules"); send_buf(wdesc->fd, "<PRE>\n"); if ((pHelp = help_lookup("RULES")) != NULL) send_buf(wdesc->fd, fix_string(smash_colour(pHelp->text))); else send_buf(wdesc->fd, "There are no rules!! Anarchy!!\n"); send_buf(wdesc->fd, "</PRE>\n"); print_footer(wdesc); } void HandleAreaRequest(WEB_DESCRIPTOR * wdesc) { AREA_DATA *pArea; int count = 0; print_header(wdesc, "Areas"); send_buf(wdesc->fd, "<TABLE>\n"); send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>Low/High<BR>Level</TD>\n"); send_buf(wdesc->fd, "<TD>Author</TD>\n"); send_buf(wdesc->fd, "<TD>Area Name</TD></TR>\n"); for (pArea = area_first;;) { if (pArea == NULL) break; if (!IS_SET(pArea->area_flags, AREA_CLOSED | AREA_PLAYER_HOMES)) { send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD>%s</TD>\n", pArea->credits); send_buf(wdesc->fd, "<TD>%s</TD></TR>\n", pArea->name); count++; } pArea = pArea->next; } send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<P>Areas Found: %d.</P>\n", count); print_footer(wdesc); } void HandleSpellsRequest(WEB_DESCRIPTOR * wdesc) { int gn; int sn, i; int tn, race; bool *displayed; int past_default = TRUE; char buf[MSL]; char buf2[MSL]; char buf3[MSL]; alloc_mem(displayed, bool, maxSkill); memset(displayed, FALSE, maxSkill); print_header(wdesc, "Spells and Skills"); for (gn = 0; gn < maxGroup; gn++) { if (group_table[gn].name == NULL) break; if (!str_cmp(group_table[gn].name, "rom basics")) { send_buf(wdesc->fd, "<H3>Basic Skills</H3>\n"); send_buf(wdesc->fd, "All players receive the <I>basic</I> skills for their class.<BR>\n"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD>Basic Group</TD>\n"); send_buf(wdesc->fd, "<TD>Skills Included</TD></TR>\n"); } if (!str_cmp(group_table[gn].name, "mage default")) { past_default = FALSE; send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<H3>Default Skill/Spell Groups</H3>\n"); send_buf(wdesc->fd, "Players receive the <I>default</I> skills and spell groups if they bypass customization.<BR>\n"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD>Default Group</TD>\n"); send_buf(wdesc->fd, "<TD>Skills Included</TD>\n"); send_buf(wdesc->fd, "<TD>Skill/Spell Groups Included</TD></TR>\n"); } if (!str_cmp(group_table[gn].name, "weaponsmaster")) { past_default = TRUE; send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<H3>Other Skill/Spell Groups</H3>\n"); send_buf(wdesc->fd, "Players may gain these skills and spells.<BR>\n"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD>Skill/Spell Group</TD>\n"); send_buf(wdesc->fd, "<TD>Skills/Spells Included</TD></TR>\n"); } under_line(buf3, group_table[gn].name); send_buf(wdesc->fd, "<TR><TD><A NAME=\"%s\">%s</A></TD><TD>", buf3, group_table[gn].name); buf[0] = '\0'; for (sn = 0; sn < MAX_IN_GROUP; sn++) { if (group_table[gn].spells[sn] == NULL) break; tn = skill_lookup(group_table[gn].spells[sn]); if (tn != -1) { snprintf(buf2, MSL, "%s, ", group_table[gn].spells[sn]); strncat(buf, buf2, MSL); displayed[tn] = TRUE; } } if (!IS_NULLSTR(buf) && (strlen(buf) > 2)) buf[(strlen(buf) - 2)] = '\0'; else strncpy(buf, "None", MSL); if (!past_default) { send_buf(wdesc->fd, "%s</TD><TD>\n", buf); buf[0] = '\0'; for (sn = 0; sn < MAX_IN_GROUP; sn++) { if (group_table[gn].spells[sn] == NULL) break; tn = skill_lookup(group_table[gn].spells[sn]); if (tn == -1) { under_line(buf3, group_table[gn].spells[sn]); snprintf(buf2, MSL, "<A HREF=\"#%s\">%s</A>, ", buf3, group_table[gn].spells[sn]); strncat(buf, buf2, MSL); } else { displayed[tn] = TRUE; } } if (!IS_NULLSTR(buf) && (strlen(buf) > 2)) buf[(strlen(buf) - 2)] = '\0'; else strncpy(buf, "None", MSL); } send_buf(wdesc->fd, "%s</TD></TR>\n", buf); } send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<H3>Other Skills and Spells</H3>\n"); send_buf(wdesc->fd, "The following skills and spells are available to various mortals.<BR>\n"); send_buf(wdesc->fd, "<TABLE><TR><TD>\n"); buf[0] = '\0'; for (sn = 1; sn < maxSkill; sn++) { if (skill_table[sn].name == NULL) break; if (!displayed[sn] && (min_class_level(sn) < ANGEL)) { snprintf(buf2, MSL, "%s, ", skill_table[sn].name); strncat(buf, buf2, MSL); displayed[sn] = TRUE; } } if (!IS_NULLSTR(buf) && (strlen(buf) > 2)) buf[(strlen(buf) - 2)] = '\0'; else strncpy(buf, "None", MSL); send_buf(wdesc->fd, "%s</TD></TR>\n", buf); send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<P>To view skills in class specific tables, visit the <a href=\"%sclass\">Classes</a> section.</P>\n", DEFAULT_URL_PORT()); send_buf(wdesc->fd, "<H3><A NAME=\"RaceSkills\">Race Skills</A></H3>\n"); send_buf(wdesc->fd, "The following skills and spells are race specific.\n"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD>Race</TD>\n"); send_buf(wdesc->fd, "<TD>Skills</TD></TR>\n"); for (race = 0; race < maxRace; race++) { if (!race_table[race].pc_race) continue; send_buf(wdesc->fd, "<TR><TD>%s</TD><TD>\n", race_table[race].name); buf[0] = '\0'; for (sn = 1; sn < maxSkill; sn++) { if (skill_table[sn].name == NULL) break; for (i = 0; i < 5; i++) { if (race_table[race].skills[i] == NULL) break; if (skill_lookup(race_table[race].skills[i]) == sn) { snprintf(buf2, MSL, "%s, ", skill_table[sn].name); strncat(buf, buf2, MSL); displayed[sn] = TRUE; } } } if (!IS_NULLSTR(buf) && (strlen(buf) > 2)) buf[(strlen(buf) - 2)] = '\0'; else strncpy(buf, "None", MSL); send_buf(wdesc->fd, "%s</TD></TR>\n", buf); } send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<H3>Immortal Skills and Spells</H3>\n"); send_buf(wdesc->fd, "The following skills and spells are available to various immortals.\n"); send_buf(wdesc->fd, "This list includes some spells under development.<BR>\n"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD>Spells</TD></TR>\n"); send_buf(wdesc->fd, "<TR><TD>\n"); buf[0] = '\0'; for (sn = 1; sn < maxSkill; sn++) { if (skill_table[sn].name == NULL) break; if (!displayed[sn]) { snprintf(buf2, MSL, "%s, ", skill_table[sn].name); strncat(buf, buf2, MSL); displayed[sn] = TRUE; } } if (!IS_NULLSTR(buf) && (strlen(buf) > 2)) buf[(strlen(buf) - 2)] = '\0'; else strncpy(buf, "None", MSL); send_buf(wdesc->fd, "%s</TD></TR>\n", buf); send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); free_mem(displayed); } void HandleWhoRequest(WEB_DESCRIPTOR * wdesc) { CHAR_DATA *wch; char buf[MSL * 2]; print_header(wdesc, "Players currently"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD><I>Level</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Race</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Class</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Clan</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Name<I></TD></TR>\n"); for (wch = player_first; wch != NULL; wch = wch->next_player) { if (wch->invis_level >= LEVEL_IMMORTAL || wch->incog_level >= LEVEL_IMMORTAL) continue; send_buf(wdesc->fd, "<TR>\n"); if (IS_NULLSTR(wch->pcdata->who_descr)) { send_buf(wdesc->fd, "<TD>%d</TD>\n", wch->level); send_buf(wdesc->fd, "<TD><A href=\"%sraces/%s\">%s</A></TD>\n", DEFAULT_URL_PORT(), race_table[wch->race].name, race_table[wch->race].name); send_buf(wdesc->fd, "<TD>%s</TD>\n", class_who(wch)); } else { html_colourconv(buf, wch->pcdata->who_descr); send_buf(wdesc->fd, "<TD COLSPAN=3>%s</TD>\n", buf); } if (is_clan(wch)) { html_colourconv(buf, clan_table[wch->clan].who_name); send_buf(wdesc->fd, "<TD><A href=\"%sclans/%s\">%s</A></TD>\n", DEFAULT_URL_PORT(), clan_table[wch->clan].name, buf); } else send_buf(wdesc->fd, "<TD></TD>\n"); send_buf(wdesc->fd, "<TD>"); send_buf(wdesc->fd, wch->name); html_colourconv(buf, wch->pcdata->title); send_buf(wdesc->fd, buf); send_buf(wdesc->fd, "</TD></TR>\n"); } send_buf(wdesc->fd, "</TABLE><BR>\n"); print_footer(wdesc); } const char *stat_type_name[MAX_GAMESTAT] = { "PLAYER KILLERS", "MOB KILLERS", "PK DEATHS", "MOB DEATHS", }; void HandleStatsRequest(WEB_DESCRIPTOR * wdesc) { int pos; print_header(wdesc, "Stats"); send_buf(wdesc->fd, "<OL>\n"); send_buf(wdesc->fd, "<li><A HREF=\"%sstats/general\">General Stats</A>\n", DEFAULT_URL_PORT()); for (pos = 0; pos < MAX_GAMESTAT; pos++) { send_buf(wdesc->fd, "<li><A HREF=\"%sstats/%s\">%s</A>\n", DEFAULT_URL_PORT(), stat_type_name[pos], stat_type_name[pos]); } send_buf(wdesc->fd, "</OL>\n"); print_footer(wdesc); } int count_statlist args((void)); int compare_stats args((const void *v1, const void *v2)); void HandleStatsTypeRequest(WEB_DESCRIPTOR * wdesc, int type) { char temp[MSL]; STAT_DATA *curr; STAT_DATA **top; int count, loop, pos = 0; bool found = FALSE; extern int compare_type; sprintf(temp, "Ranking of %s", stat_type_name[type]); print_header(wdesc, temp); send_buf(wdesc->fd, "<TABLE>\n"); alloc_mem(top, STAT_DATA *, count_statlist()); count = 0; compare_type = type; loop = 0; for (curr = stat_first; curr != NULL; curr = curr->next) { top[count] = curr; count++; found = TRUE; } qsort(top, count, sizeof(*top), compare_stats); send_buf(wdesc->fd, "<TR><TD><I>Rank</I></TD><TD><I>Name</I></TD><TD><I>Number</I></TD>" "<TD><I>Rank</I></TD><TD><I>Name</I></TD><TD><I>Number</I></TD></TR>"); for (loop = 0; loop < count; loop++) { if (loop >= 50) break; sprintf(temp, "%s<TD>%2d)</TD><TD>%-20s</TD><TD>%ld</TD>\n", pos == 0 ? "<TR>" : "", loop + 1, top[loop]->name, top[loop]->gamestat[type]); send_buf(wdesc->fd, temp); if (++pos % 2 == 0) { send_buf(wdesc->fd, "</TR>"); pos = 0; } } if (!found) send_buf(wdesc->fd, "<TR><TD COLSPAN=3>No one found yet.</TD></TR>\n"); send_buf(wdesc->fd, "</TABLE>"); send_buf(wdesc->fd, "<BR><A href=\"%sstats\">Back to Stats Index</A>\n", DEFAULT_URL_PORT()); print_footer(wdesc); free_mem(top); } void HandleCommandsRequest(WEB_DESCRIPTOR * wdesc) { int i; int pos = 0; HELP_DATA *pHelp; int count = 0; struct cmd_type *commands = NULL; print_header(wdesc, "Commands"); send_buf(wdesc->fd, "<TABLE>\n"); alloc_mem(commands, struct cmd_type, maxCommands + 1); commands = (struct cmd_type *) memcpy(commands, cmd_table, sizeof(struct cmd_type) * maxCommands + 1); qsort(commands, maxCommands, sizeof(cmd_table[0]), srt_cmds_name); for (i = 0; i < maxCommands; i++) { if (commands[i].level >= LEVEL_IMMORTAL || !commands[i].show) continue; count = 0; for (pHelp = help_first; pHelp; pHelp = pHelp->next) { count++; if (is_name(commands[i].name, pHelp->keyword)) break; } if (pHelp) send_buf(wdesc->fd, "%s<TD><A href=\"%shelps/%d\">%s</TD>\n", (pos == 0) ? "<TR>" : "", DEFAULT_URL_PORT(), count, commands[i].name); else send_buf(wdesc->fd, "%s<TD>%s</TD>\n", (pos == 0) ? "<TR>" : "", commands[i].name); if (++pos % 5 == 0) { send_buf(wdesc->fd, "</TR>"); pos = 0; } } send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); free_mem(commands); } void HandleHelpsRequest(WEB_DESCRIPTOR * wdesc) { HELP_DATA *pHelp; int count = 0; int pos = 0; print_header(wdesc, "Help Files"); send_buf(wdesc->fd, "<TABLE>\n"); for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next) { count++; if (pHelp->level <= LEVEL_HERO && pHelp->level >= 0) { const char *temp; char wordkey[MSL]; temp = pHelp->keyword; while (!IS_NULLSTR(temp)) { wordkey[0] = '\0'; temp = one_argument(temp, wordkey); send_buf(wdesc->fd, "%s<TD><A HREF=\"%shelps/%d\">%s</A></TD>\n", (pos == 0) ? "<TR>" : "", DEFAULT_URL_PORT(), count, wordkey); if (++pos % 5 == 0) { send_buf(wdesc->fd, "</TR>"); pos = 0; } } } } send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); } void HandleHelpDumpRequest(WEB_DESCRIPTOR * wdesc, HELP_DATA * pHelp) { char buf[MSL * 5]; print_header(wdesc, pHelp->keyword); send_buf(wdesc->fd, "<TABLE>\n"); send_buf(wdesc->fd, "<TR><TD>[%d] %s<TD></TR>\n", pHelp->level, pHelp->keyword); html_colourconv(buf, fix_string(pHelp->text)); send_buf(wdesc->fd, "<TR><TD>%s</TD></TR>\n", buf); send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<BR><A HREF=\"%shelps\">Back to Help Index</A>\n", DEFAULT_URL_PORT()); print_footer(wdesc); } void HandleRaceRequest(WEB_DESCRIPTOR * wdesc) { int race; print_header(wdesc, "Races"); send_buf(wdesc->fd, "<TABLE><TR>\n"); send_buf(wdesc->fd, "<TD><I>Race</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Str</I></TD><TD><I>Int</I></TD><TD><I>Wis</I></TD><TD><I>Dex</I></TD><TD><I>Con</I></TD><TD><I>Luck</I></TD>\n"); send_buf(wdesc->fd, "<TD><I>Creation<BR>Points</I></TD></TR>\n"); for (race = 0; race_table[race].name != NULL; race++) { if (!race_table[race].pc_race) continue; send_buf(wdesc->fd, "<TR>\n"); send_buf(wdesc->fd, "<TD><A HREF=\"%s" "races/%s\">%s</A></TD>\n<TD>%d</TD>\n<TD>%d</TD>\n<TD>%d</TD>\n" "<TD>%d</TD>\n<TD>%d</TD><TD ALIGN=\"center\">%d</TD></TR>\n", DEFAULT_URL_PORT(), race_table[race].name, race_table[race].name, race_table[race].max_stats[STAT_STR], race_table[race].max_stats[STAT_INT], race_table[race].max_stats[STAT_WIS], race_table[race].max_stats[STAT_DEX], race_table[race].max_stats[STAT_CON], race_table[race].points); } send_buf(wdesc->fd, "</TABLE>\n"); send_buf(wdesc->fd, "<P>Creation points increase the amount of experience it takes to gain a level. Maximum a stat can go is 30.</P>\n"); send_buf(wdesc->fd, "<P>To view skills and spells available to each race, visit the <a href=\"%sspells#RaceSkills\">Skill/Spell</A> section.</P>\n", DEFAULT_URL_PORT()); print_footer(wdesc); } void HandleRaceDumpRequest(WEB_DESCRIPTOR * wdesc, int race) { HELP_DATA *pHelp; char buf[MSL * 2]; print_header(wdesc, race_table[race].name); sprintf(buf, "%s race", race_table[race].name); if ((pHelp = help_lookup(buf)) != NULL) { html_colourconv(buf, pHelp->text); send_buf(wdesc->fd, "<P>%s</P><BR>\n", buf); } else send_buf(wdesc->fd, "<P>No Info Available</P><BR>\n"); print_footer(wdesc); } void HandleClanRequest(WEB_DESCRIPTOR * wdesc) { int clan; char buf[MSL * 3]; print_header(wdesc, "Clans"); send_buf(wdesc->fd, "<TABLE>\n"); send_buf(wdesc->fd, "<TR><B><TD><I>Name</I></TD><TD><I>Description</I></TD><TD><I>Homepage</I></TD></TR>\n"); for (clan = 0; clan < maxClan; clan++) { html_colourconv(buf, clan_table[clan].who_name); send_buf(wdesc->fd, "<TR><TD>%s</TD></TR>\n", buf); } send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); } void HandleClassRequest(WEB_DESCRIPTOR * wdesc) { int iClass; print_header(wdesc, "Classes"); send_buf(wdesc->fd, "<TABLE>\n"); for (iClass = 0; iClass < maxClass; iClass++) { send_buf(wdesc->fd, "<TR><TD><A HREF=\"%sclass/%s\">%s</A>" "</TD></TR>\n", DEFAULT_URL_PORT(), class_table[iClass].name, class_table[iClass].name); } send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); } void HandleClassDumpRequest(WEB_DESCRIPTOR * wdesc, int Class) { char buf[MSL * 2], buf2[MSL]; char skill_list[LEVEL_HERO + 1][MSL]; int snc, lev; HELP_DATA *pHelp; print_header(wdesc, class_table[Class].name); if ((pHelp = help_lookup(class_table[Class].name)) != NULL) { html_colourconv(buf, pHelp->text); send_buf(wdesc->fd, "<TABLE><TR><TD>%s</TD></TR></TABLE>\n", buf); } send_buf(wdesc->fd, "<TABLE>\n"); for (lev = 0; lev < LEVEL_HERO + 1; lev++) skill_list[lev][0] = '\0'; for (snc = 0; snc < maxSkill; snc++) { if (skill_table[snc].name == NULL) break; if ((lev = skill_table[snc].skill_level[Class]) <= LEVEL_HERO) { sprintf(buf2, "%s, ", skill_table[snc].name); if (IS_NULLSTR(skill_list[lev])) sprintf(skill_list[lev], "<TR><TD>Level %d</TD><TD>%s", lev, buf2); else strncat(skill_list[lev], buf2, MSL); } } for (lev = 0; lev < LEVEL_HERO + 1; lev++) { if (skill_list[lev][0] != '\0') { if (strlen(skill_list[lev]) > 2) skill_list[lev][(strlen(skill_list[lev]) - 2)] = '\0'; send_buf(wdesc->fd, skill_list[lev]); send_buf(wdesc->fd, "</TD></TR>\n"); } } send_buf(wdesc->fd, "</TABLE>\n"); print_footer(wdesc); } void HandleNotesRequest(WEB_DESCRIPTOR * wdesc) { int pos = 0; print_header(wdesc, "Notes"); send_buf(wdesc->fd, "<OL>\n"); for (pos = 0; pos < MAX_BOARD - 3; pos++) { send_buf(wdesc->fd, "<li><A HREF=\"%snotes/%s\">%s</A>\n", DEFAULT_URL_PORT(), boards[pos].short_name, boards[pos].short_name); } send_buf(wdesc->fd, "</OL>\n"); print_footer(wdesc); } void HandleNotesDumpRequest(WEB_DESCRIPTOR * wdesc, BOARD_DATA * board) { NOTE_DATA *pnote; char buf[MSL * 5]; sprintf(buf, "Notes on %s Board", board->short_name); print_header(wdesc, buf); for (pnote = board->note_first; pnote != NULL; pnote = pnote->next) { if (!is_name(pnote->to_list, "all")) continue; html_colourconv(buf, pnote->text); send_buf(wdesc->fd, "<TABLE><TR><TD><b>%s</b></TD><TD>%s</TD></TR><TR><TD><B>Date</B></TD>\n" "<TD>%s</TD></TR><TR><TD COLSPAN=2>%s</TD><TR></TABLE><BR>\n", pnote->sender, pnote->subject, pnote->date, buf); } send_buf(wdesc->fd, "<BR><A href=\"%snotes\">Back to Board Index</A>\n", DEFAULT_URL_PORT()); print_footer(wdesc); } void HandleSkDebugRequest(WEB_DESCRIPTOR * wdesc) { int i, sn; print_header(wdesc, "Skill List"); send_buf(wdesc->fd, "<TABLE><TR><TD>Skill Name</TD>\n"); for (i = 0; i < maxClass; i++) send_buf(wdesc->fd, "<TD>%s</TD>\n", class_table[i].name); send_buf(wdesc->fd, "</TR>\n"); for (sn = 0; sn < maxSkill; sn++) { send_buf(wdesc->fd, "<TR><TD>%s</TD>\n", skill_table[sn].name); for (i = 0; i < maxClass; i++) { if (skill_table[sn].skill_level[i] >= LEVEL_IMMORTAL) send_buf(wdesc->fd, "<TD>---</TD>\n"); else send_buf(wdesc->fd, "<TD>%d</TD>\n", skill_table[sn].skill_level[i]); } send_buf(wdesc->fd, "</TR>\n"); } send_buf(wdesc->fd, "</TABLE>"); print_footer(wdesc); } void HandleLogRequest(WEB_DESCRIPTOR * wdesc) { struct dirent *Dir; DIR *Directory; char buf[MSL]; int count = 0; print_header(wdesc, "Log Files"); send_buf(wdesc->fd, "<TABLE><TR>\n"); Directory = opendir("../log"); Dir = readdir(Directory); while (Dir != NULL) { if (!str_suffix(".log", Dir->d_name)) { strncpy(buf, Dir->d_name, MSL); buf[strlen(buf) - 4] = '\0'; send_buf(wdesc->fd, "%s<TD><A href=\"%s%s/log/%s\">%s</A></TD>\n", count % 5 == 0 ? "</TR><TR>" : "", DEFAULT_URL_PORT(), SECURE_URL, buf, Dir->d_name); count++; } Dir = readdir(Directory); } closedir(Directory); send_buf(wdesc->fd, "%s</TABLE>\n", count % 5 != 0 ? "</TR>" : ""); print_footer(wdesc); } void HandleLogDumpRequest(WEB_DESCRIPTOR * wdesc, char *file) { char buf[MSL]; print_header(wdesc, file); send_buf(wdesc->fd, "<PRE>\n"); sprintf(buf, "../log/%s.log", file); print_file(wdesc, buf); send_buf(wdesc->fd, "</PRE>\n"); print_footer(wdesc); } void HandleObjsRequest(WEB_DESCRIPTOR * wdesc, OBJ_INDEX_DATA * pObj) { char buf[MSL * 3]; print_header(wdesc, smash_colour(pObj->short_descr)); html_colourconv(buf, pObj->short_descr); send_buf(wdesc->fd, "<P>%s<br>", buf); html_colourconv(buf, pObj->description); send_buf(wdesc->fd, "%s<br>", buf); send_buf(wdesc->fd, "Material: %s<br>", pObj->material); send_buf(wdesc->fd, "Type: %s<br></P>", item_name(pObj->item_type)); print_footer(wdesc); } void HandleMobsRequest(WEB_DESCRIPTOR * wdesc, MOB_INDEX_DATA * pMob) { char buf[MSL * 3]; print_header(wdesc, smash_colour(pMob->short_descr)); html_colourconv(buf, pMob->short_descr); send_buf(wdesc->fd, "<P>%s<br>", buf); html_colourconv(buf, pMob->description); send_buf(wdesc->fd, "%s<br>", buf); send_buf(wdesc->fd, "Race: %s<br>", race_table[pMob->race].name); send_buf(wdesc->fd, "Sex: %s<br>", sex_table[pMob->sex].name); send_buf(wdesc->fd, "Alignment: %d<br>", pMob->alignment); send_buf(wdesc->fd, "</P>"); print_footer(wdesc); } void HandleRoomsRequest(WEB_DESCRIPTOR * wdesc, vnum_t room) { ROOM_INDEX_DATA *pRoom = get_room_index(room); EXIT_DATA *pexit; int door; char buf[MSL * 4]; if (!pRoom) return; print_header(wdesc, pRoom->name); send_buf(wdesc->fd, "<TABLE><TR><TD colspan=3>"); html_colourconv(buf, pRoom->name); send_buf(wdesc->fd, buf); send_buf(wdesc->fd, "</TD><TD colspan=3>%s", pRoom->area->name); send_buf(wdesc->fd, "</TD></TR><TR><TD colspan=6>"); html_colourconv(buf, pRoom->description); send_buf(wdesc->fd, buf); send_buf(wdesc->fd, "</TD></TR><TR>"); for (door = 0; door < MAX_DIR; door++) { if ((pexit = pRoom->exit[door]) != NULL && pexit->u1.to_room != NULL) { send_buf(wdesc->fd, "<TD><A href=\"%srooms/%ld\">%s</A></TD>", DEFAULT_URL_PORT(), pexit->u1.to_room->vnum, dir_name[door]); } else send_buf(wdesc->fd, "<TD></TD>"); } send_buf(wdesc->fd, "</TR><TR><TD colspan=6>%s</TD>", show_list_to_html(pRoom->first_content)); send_buf(wdesc->fd, "</TR><TR><TD colspan=6>%s</TD>", show_char_to_html(pRoom->first_person)); send_buf(wdesc->fd, "</TR></TABLE>"); print_footer(wdesc); } void HandleSocialsRequest(WEB_DESCRIPTOR * wdesc) { int iSocial; print_header(wdesc, "Socials"); send_buf(wdesc->fd, "<TABLE><TR>\n"); for (iSocial = 0; !IS_NULLSTR(social_table[iSocial].name); iSocial++) send_buf(wdesc->fd, "%s<TD>%s</TD>\n", iSocial % 5 == 0 ? "</TR><TR>" : "", social_table[iSocial].name); send_buf(wdesc->fd, "%s</TABLE>\n", iSocial % 5 != 0 ? "</TR>" : ""); print_footer(wdesc); } void HandleImmRequest(WEB_DESCRIPTOR * wdesc) { print_header(wdesc, "Immortal Info Page"); send_buf(wdesc->fd, "<hr><P><A href=\"%s%s/skdebug\">Skill Debug List</A></P>", DEFAULT_URL_PORT(), SECURE_URL); send_buf(wdesc->fd, "<P><A href=\"%s%s/log\">Log Files</A></P>", DEFAULT_URL_PORT(), SECURE_URL); send_buf(wdesc->fd, "<hr>"); print_footer(wdesc); } void HandleImmInvalid(WEB_DESCRIPTOR * wdesc) { print_header(wdesc, "Invalid Username/Password"); send_buf(wdesc->fd, "<P>Invalid username/password. Each field is Case Sensitive.</P>\n"); print_footer(wdesc); } void handle_web_request(WEB_DESCRIPTOR * wdesc) { char tpath[MSL]; const char *stuff; char *path; int addr; char web_buf[MSL]; stuff = first_arg(wdesc->request, tpath, FALSE); first_arg(stuff, tpath, FALSE); path = tpath; path++; /* process request */ /* are we using HTTP/1.x? If so, write out header stuff.. */ if (!strstr(wdesc->request, "GET")) { send_buf(wdesc->fd, "HTTP/1.1 501 Not Implemented"); return; } else if (strstr(wdesc->request, "HTTP/1.")) { if (!str_prefix(SECURE_URL, path)) { char *where; char encoded[MIL]; char username[MIL]; char *password = &str_empty[0]; username[0] = '\0'; encoded[0] = '\0'; where = strstr(stuff, "Authorization: Basic"); if (!where) send_401UNAUTHORISED(wdesc, AUTH_DOMAIN); else { where += strlen("Authorization: Basic"); where++; for (password = encoded; *where && !isspace(*where); where++, password++) *password = *where; *password = '\0'; Base64Decode(encoded, (unsigned char *) username, MIL); for (password = username; *password && *password != ':'; password++); { if (*password == ':') { *password = '\0'; password++; } } } if (check_web_pass(username, password)) { char *buf; if (!str_cmp(path, SECURE_URL) || !str_cmp(path, SECURE_URL "/")) { logf("Web Request: %s - Secure listing", web_buf); HandleImmRequest(wdesc); return; } buf = strchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; if (!str_cmp(buf, "skdebug") || !str_cmp(buf, "skdebug/")) { logf("Web Request: %s - Skill Debug listing", web_buf); HandleSkDebugRequest(wdesc); return; } else if (!str_prefix("log", buf)) { char buf2[MSL]; char *check; DIR *Directory; struct dirent *Dir; if (!str_cmp(buf, "log") || !str_cmp(buf, "log/")) { logf("Web Request: %s - Log listing", web_buf); HandleLogRequest(wdesc); return; } Directory = opendir("../log"); Dir = readdir(Directory); check = strrchr(buf, '/'); if (!IS_NULLSTR(check)) { check++; while (Dir != NULL) { if (!str_suffix(".log", Dir->d_name)) { strncpy(buf2, Dir->d_name, MSL); buf2[strlen(buf2) - 4] = '\0'; if (!str_cmp(buf2, check)) { logf("Web Request: Log %s", buf2); HandleLogDumpRequest(wdesc, buf2); return; } } Dir = readdir(Directory); } } closedir(Directory); goto unknown; } } goto unknown; } else { HandleImmInvalid(wdesc); return; } } } addr = ntohl(wdesc->their_addr.sin_addr.s_addr); strcpy(web_buf, inet_ntoa(wdesc->their_addr.sin_addr)); if (!str_cmp(path, "online") || !str_cmp(path, "online/")) { logf("Web Request: %s - Who listing", web_buf); HandleWhoRequest(wdesc); } else if (!str_cmp(path, "tech") || !str_cmp(path, "tech/")) { logf("Web Request: %s - Memory status", web_buf); HandleMemoryRequest(wdesc); } else if (!str_cmp(path, "areas") || !str_cmp(path, "areas/")) { logf("Web Request: %s - Area listing", web_buf); HandleAreaRequest(wdesc); } else if (!str_cmp(path, "spells") || !str_cmp(path, "spells/")) { logf("Web Request: %s - Spell listing", web_buf); HandleSpellsRequest(wdesc); } else if (!str_cmp(path, "rules") || !str_cmp(path, "rules/")) { logf("Web Request: %s - Rules listing", web_buf); HandleRulesRequest(wdesc); } else if (!str_cmp(path, "socials") || !str_cmp(path, "socials/")) { logf("Web Request: %s - Socials", web_buf); HandleSocialsRequest(wdesc); } else if (!str_prefix("helps", path)) { HELP_DATA *pHelp; char *buf; int pos; char temp[MIL]; if (!str_cmp(path, "helps") || !str_cmp(path, "helps/")) { logf("Web Request: %s - Help listing", web_buf); HandleHelpsRequest(wdesc); return; } buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; pos = 0; for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next) { pos++; sprintf(temp, "%d", pos); if (!str_cmp(buf, temp)) { logf("Web Request: %s - Help File %s", pHelp->keyword, web_buf); HandleHelpDumpRequest(wdesc, pHelp); return; } } } goto unknown; } else if (!str_prefix("notes", path)) { char *buf; int pos; if (!str_cmp(path, "notes") || !str_cmp(path, "notes/")) { logf("Web Request: %s - Note listing", web_buf); HandleNotesRequest(wdesc); return; } buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; for (pos = 0; pos < MAX_BOARD - 3; pos++) { if (!str_cmp(boards[pos].short_name, buf)) { logf("Web Request: %s - Note Board %s", web_buf, boards[pos].short_name); HandleNotesDumpRequest(wdesc, &boards[pos]); return; } } } goto unknown; } else if (!str_prefix("races", path)) { int race; char *buf; if (!str_cmp(path, "races") || !str_cmp(path, "races/")) { logf("Web Request: %s - Race listing", web_buf); HandleRaceRequest(wdesc); return; } buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; for (race = 0; race < maxRace; race++) { if (!str_cmp(race_table[race].name, buf)) { logf("Web Request: %s - Race %s", web_buf, race_table[race].name); HandleRaceDumpRequest(wdesc, race); return; } } } goto unknown; } else if (!str_cmp(path, "commands") || !str_cmp(path, "commands/")) { logf("Web Request: %s - Commands listing", web_buf); HandleCommandsRequest(wdesc); return; } else if (!str_prefix("clans", path)) { logf("Web Request: %s - Clan listing", web_buf); HandleClanRequest(wdesc); return; } else if (!str_prefix("class", path)) { int i; char *buf; if (!str_cmp(path, "class") || !str_cmp(path, "class/")) { logf("Web Request: %s - Class listing", web_buf); HandleClassRequest(wdesc); return; } buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; for (i = 0; i < maxClass; i++) { if (!str_cmp(class_table[i].name, buf)) { logf("Web Request: %s - Class %s", web_buf, class_table[i].name); HandleClassDumpRequest(wdesc, i); return; } } } goto unknown; } else if (!str_prefix("chars", path)) { MOB_INDEX_DATA *pMob; char *buf; buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; if (!is_number(buf)) { logf("Request: %s - Bad mob vnum", web_buf); goto unknown; } if ((pMob = get_mob_index(atol(buf))) != NULL) { logf("Web Request: %s - Mob %ld", web_buf, pMob->vnum); HandleMobsRequest(wdesc, pMob); return; } } goto unknown; } else if (!str_prefix("objs", path)) { OBJ_INDEX_DATA *pObj; char *buf; buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; if (!is_number(buf)) { logf("Request: %s - Bad obj vnum", web_buf); goto unknown; } if ((pObj = get_obj_index(atol(buf))) != NULL) { logf("Web Request: %s - Obj %ld", web_buf, pObj->vnum); HandleObjsRequest(wdesc, pObj); return; } } goto unknown; } else if (!str_prefix("rooms", path)) { ROOM_INDEX_DATA *pRoom; if (!str_cmp("rooms", path)) { logf("Web Request: %s - Rooms listing", web_buf); HandleRoomsRequest(wdesc, ROOM_VNUM_TEMPLE); return; } else { char *buf; buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; if (!is_number(buf)) { logf("Request: %s - Bad room vnum", web_buf); goto unknown; } if ((pRoom = get_room_index(atol(buf))) != NULL) { if (!IS_SET (pRoom->area->area_flags, AREA_CLOSED | AREA_PLAYER_HOMES)) { logf("Web Request: %s - Room %ld", web_buf, pRoom->vnum); HandleRoomsRequest(wdesc, pRoom->vnum); return; } } } goto unknown; } } else if (!str_prefix("stats", path)) { int pos; char *buf; if (!str_cmp(path, "stats") || !str_cmp(path, "stats/")) { logf("Web Request: %s - Stats listing", web_buf); HandleStatsRequest(wdesc); return; } buf = strrchr(path, '/'); if (!IS_NULLSTR(buf)) { buf++; for (pos = 0; pos < MAX_GAMESTAT; pos++) { if (!str_cmp(stat_type_name[pos], buf)) { logf("Web Request: %s - Stats %s", web_buf, stat_type_name[pos]); HandleStatsTypeRequest(wdesc, pos); return; } } } goto unknown; } else { unknown: print_header(wdesc, "Error"); logf("Unknown Request [%s] by %s", path, web_buf); send_buf(wdesc->fd, "Unknown url '%s'.<BR>", path); print_footer(wdesc); } tail_chain(); } void shutdown_web_server(void) { WEB_DESCRIPTOR *current, *next; /* Stop Listening */ log_string("Closing webserver..."); close(web_socket); web_socket = -1; WebUP = FALSE; /* Close All Current Connections */ for (current = first_webdesc; current; current = next) { next = current->next; close(current->fd); UNLINK(current, first_webdesc, last_webdesc, next, prev); free_mem(current); } }