/**************************************************************************/ // websrv.cpp - Integrated webserver written by Michael Garratt /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * **************************************************************************/ #include "network.h" #include "websrv.h" #include "include.h" #include "nanny.h" #include "colour.h" #include "gameset.h" #include "bitflags.h" #define LOGALLWEB false #define IS_NULLSTR(str) ((str)==NULL || (str)[0]=='\0') // prototypes void logWebf (char * fmt, ...); void logWeb( const char *str ); void filterWebRequest(connection_data *w); int is_space( char c ); void closeConnection(web_request_data *w); bool readFromWebConnection ( web_request_data *w ); extern int webHits; extern int webHelpHits; extern int webWhoHits; //mud2web.cpp char *getwebAreas(int level); char *getwebWho(int level); char *getwebWizlist(); char *getwebSockets(int level); char *getwebMudstats(int level); char *getwebClassStats(); char *getwebRaceStats(); char *getwebRaceInfo(); char *getwebClassInfo(char *racename); char *getwebAlignStats(); char *getwebLastonImm(int level); char *getwebHelp(char *, char *entry_buf, int length); char *getwebGameSettings(); // laston.cpp int laston_web_level(char *username, char *password); // handler.cpp void append_string_to_file( const char *file, const char *str, bool newline ); // dawnlib.cpp int str_len(const char *s); /**************************************************************************/ void badRequestFormat(web_request_data *w, char * reason){ w->outBodyLen=sprintf(w->outBodyBuf, "badRequestFormat: %s\r\n", reason); return; } /**************************************************************************/ void processHTTP0_9Request(web_request_data *w){ w->outBodyLen=sprintf(w->outBodyBuf,"HTTP/0.9 is not supported (or bad request)\r\n"); return; } /**************************************************************************/ // returns true if there was an error bool processWebHeader(web_request_data *w){ // NEEDS TO PROCESS THE HEADER FROM pCRLFCRLF char *pStr, *pLine; char workingline[WEBIN_HEADER_BUF+100]; char method[21]; // TODO - tidy up check request isn't too long if (w->inHeaderLen> WEBIN_HEADER_BUF){ badRequestFormat(w, "Request-Line too long"); return false; } strncpy(workingline, w->inHeaderBuf, w->inHeaderLen); workingline[w->inHeaderLen]='\0'; pLine=workingline; // copy parameters, checking the // length of each command as we go // extract the METHOD pStr=strstr( pLine, " "); if (!pStr){ badRequestFormat(w, "Request-Line invalid"); return true; } if (pStr-pLine>20){ badRequestFormat(w, "method in Request-Line too long"); return true; } strncpy(method, pLine, pStr-pLine); method[pStr-pLine]='\0'; pLine=pStr+1; // extract the LOCATION pStr=strstr( pLine, " "); if (!pStr){ badRequestFormat(w, "Request-Line invalid"); return true; } if (pStr-pLine>MAXLOCATION){ badRequestFormat(w, "Request-URI (location) in Request-Line too long"); return true; } strncpy(w->requestedLocation, pLine, pStr-pLine); w->requestedLocation[pStr-pLine]='\0'; pLine=pStr+1; // extract the VERSION if (strstr( pLine, "HTTP/" )==NULL) { badRequestFormat(w, "no HTTP version in Request-Line"); } if (str_len(pLine)>20){ badRequestFormat(w, "HTTP-Version in Request-Line too long"); return true; } strcpy(w->version, pLine); // now identify the method if (!strcmp(method,"GET")){ w->requestType=GET; }else if (!strcmp(method,"HEAD")){ w->requestType=HEAD; }else if (!strcmp(method,"POST")){ w->requestType=POST; }else{ w->requestType=UNDEFINED; } // check for an "Authorization:" header pLine=strstr( w->inHeaderBuf, "Authorization:"); if (pLine){ // we have an Authorization - process it pStr=strstr( pLine, "\r\n"); if (pStr){ strncpy(workingline, pLine, pStr-pLine); workingline[pStr-pLine]='\0'; pLine=workingline; //we have the authorization string in newline logWebf("Processing Authorization: '%s'",workingline); pStr=strstr( pLine, "Basic "); if (pStr){ pLine=pStr+6; if (str_len(pLine)>0 && str_len(pLine)<200){ char decoded[200]; char *d; d =decodeBase64(pLine); if (d){ sprintf(decoded,d); // now separate the username:password pLine=decoded; pStr=strstr( pLine, ":"); if (pStr){ if (pStr-pLine<MAXPASSLENGTH){ strncpy(w->username, pLine, pStr-pLine); w->username[pStr-pLine]='\0'; pLine=pStr+1; if (str_len(pLine)>MAXPASSLENGTH){ // not a valid password - so authuenticate invalid w->username[0]='\0'; }else{ strcpy(w->password, pLine); } } } } } }else{ logWeb("Authorization type not recognised"); } } } return true; } /**************************************************************************/ void greet_http(connection_data *c) { logWebf("New web connection from %s", c->remote_ip); web_request_data *w=new web_request_data; w->receivingHeader=true; w->inHeaderBuf[0]='\0'; w->inHeaderLen=0; w->inBodyBuf[0]='\0'; w->inBodyLen=0; w->outHeaderBuf[0]='\0'; w->outHeaderLen=0; w->outBodyBuf[0]='\0'; w->outBodyLen=0; w->contentLength=0; // set some defaults w->username[0]='\0'; w->password[0]='\0'; w->requestType= UNDEFINED; w->contentLength=0;// request message doesn't have a body w->statusCode=503; // service unavailable w->contentType= CONTENT_TYPE_HTML; c->web_request=w; } /**************************************************************************/ // return true if everything is okay - i.e. we read something or are // waiting for something. bool websrv_process_input(connection_data *c) { int bytesIn; char *pDest; int maxDestLength; int *currentLength; if (c->web_request->receivingHeader){ maxDestLength =WEBIN_HEADER_BUF-c->web_request->inHeaderLen; pDest = &c->web_request->inHeaderBuf[c->web_request->inHeaderLen]; currentLength=&c->web_request->inHeaderLen; }else{ maxDestLength=WEBIN_BODY_BUF-c->web_request->inBodyLen; pDest = &c->web_request->inBodyBuf[c->web_request->inBodyLen]; currentLength=&c->web_request->inBodyLen; } bytesIn=recv(c->connected_socket, pDest, maxDestLength,0); if (bytesIn>0){ logWeb("Getting input"); *(pDest+bytesIn)='\0'; *currentLength+=bytesIn; if(LOGALLWEB){ logWeb(c->web_request->inHeaderBuf); } if (c->web_request->inHeaderLen>=WEBIN_HEADER_BUF){ logWebf("Buffer overflow from %s",c->remote_ip); logWeb(c->web_request->inHeaderBuf); } filterWebRequest(c); }else{ // connection closed at the other end logWebf("closing connection - 0 bytes in (%d).", bytesIn); connection_close( c ); return false; } return true; } /**************************************************************************/ void websrv_process_output(connection_data *c) { if (c->web_request->outBodyLen>0){ send(c->connected_socket, &c->web_request->outHeaderBuf[0], c->web_request->outHeaderLen, 0); send(c->connected_socket, &c->web_request->outBodyBuf[0], c->web_request->outBodyLen, 0); logWeb("closing connection - sent data."); connection_close(c); } } /**************************************************************************/ /**************************************************************************/ /* * Writes a string to the log. */ void logWeb( const char *str ) { logWebf("%s", str); return; } /**************************************************************************/ // create a HTTP/1.0 header void createHeader(web_request_data *w){ char reason[50]; time_t ltime; char GMTdate[100]; // get the date first time( <ime ); strftime(GMTdate, 99, "%A, %d-%b-", gmtime( <ime )); char t2buf[20]; // budget fix to get rid of y2k compiler warning strftime(t2buf, 20, "%Y %T GMT", gmtime( <ime )); strcat(GMTdate,&t2buf[2]); switch (w->statusCode){ // 2xx: Success case 200: sprintf(reason, "OK"); break; case 201: sprintf(reason, "Created"); break; case 202: sprintf(reason, "Accepted"); break; case 204: sprintf(reason, "No Content"); break; // 3xx: Redirection case 301: sprintf(reason, "Moved Permanently"); break; case 302: sprintf(reason, "Moved Temporarily"); break; case 304: sprintf(reason, "Not Modified"); break; // 4xx: Client Error case 400: sprintf(reason, "Bad Request"); break; case 401: sprintf(reason, "Unauthorized"); break; case 403: sprintf(reason, "Forbidden"); break; case 404: sprintf(reason, "Not Found"); break; // 5xx: Server Error case 500: sprintf(reason, "Internal Server Error"); break; case 501: sprintf(reason, "Not Implemented"); break; case 502: sprintf(reason, "Bad Gateway"); break; case 503: sprintf(reason, "Service Unavailable"); break; default: w->statusCode=500; sprintf(reason, "Internal Server Error"); break; } w->outHeaderLen= sprintf(w->outHeaderBuf, "HTTP/1.0 %d %s\r\n" "Date: %s\r\n" "WWW-Authenticate: Basic realm=\"%s Integrated WebServer\"\r\n" "Cache-Control: private, pre-check=0, post-check=0, max-age=0\r\n" "Expires: %s\r\n" "Last-Modified: %s\r\n" "Content-Type: %s\r\n" "Content-Length: %d\r\n" "\r\n" ,w->statusCode, reason, GMTdate, MUD_NAME, GMTdate, GMTdate, w->contentType==CONTENT_TYPE_HTML?"text/html":"text/plain", w->outBodyLen); } /**************************************************************************/ char *websvr_generate_head_and_start_body(char *title) { static char head[8192]; sprintf(head, DOCTYPE "<HTML><HEAD>\n" "<TITLE>Dawn - %.8000s</TITLE>\n" "<link rel=\"StyleSheet\" href=\"/stat.css\" type=\"text/css\" />\n" "</HEAD>\n" "\n" "<BODY BGCOLOR=\"#000000\" TEXT=\"#BFBFBF\">\n", title); return head; } /**************************************************************************/ void websvr_handle_insufficient_immweb_level(web_request_data *w, char *buf, int immweb_level) { if (immweb_level==-1){ w->statusCode=200; buf=process_colour("`WYou haven't set a webpassword for your character,`1" "Use the webpass command within the game to do this.", NULL); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<PRE><FONT>%s</PRE>" "<p><A HREF=\"/\">[Back to root]</A> <A HREF=\"/sockets\">[sockets]</A> <A HREF=\"/mudstats\">[mudstats]</A>" ,websvr_generate_head_and_start_body("web password setting required!") ,buf); }else{ w->statusCode=401; logWeb("401 - Authorisation denied"); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "<html><head><title>Incorrect username/password</title></head>\n" "<body><h1>Incorrect username/password</h1>\n" "<p>You got the username or password incorrect.\n" "<p><A HREF=\"/\">Back to root</A>"); } } /**************************************************************************/ // This function is the most ugly piece of code I have written in my // life, it was a hack to get things working quickly then evolved // into this hideous beast - Kal, Oct 2001. void processGET(web_request_data *w) { char *buf=""; webHits++; if(!str_cmp(w->requestedLocation,"/stat.css")){ // stats.css - the style sheet w->statusCode=200; w->contentType=CONTENT_TEXT_PLAIN; strcpy(w->outBodyBuf, IS_NULLSTR(game_settings->style_sheet)?"":game_settings->style_sheet); w->outBodyLen=str_len(w->outBodyBuf); }else if (!str_cmp(w->requestedLocation,"/areas")){ webWhoHits++; w->statusCode=200; buf=getwebAreas(20); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<H2><FONT COLOR=\"#BFBFBF\">Areas within %s</FONT></H2>" "\n<PRE><FONT>%s" " [<A HREF=\"/\">back</A>]\n" "</PRE>" ,websvr_generate_head_and_start_body("Areas") ,MUD_NAME ,buf); }else if (!str_cmp(w->requestedLocation,"/who")){ webWhoHits++; w->statusCode=200; buf=getwebWho(20); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<H2><FONT COLOR=\"#BFBFBF\">Who is online %s</FONT></H2>" "\n<PRE><FONT>%s" " [<A HREF=\"/\">back</A>]\n" "</PRE>" ,websvr_generate_head_and_start_body("Wholist") ,MUD_NAME ,buf); }else if (!str_cmp(w->requestedLocation,"/mudstats")){ w->statusCode=200; buf=getwebMudstats(20); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<p><A HREF=\"http://www.dawnoftime.org/\">Go to the official Dawn of Time website</A>\n" " <A HREF=\"/\">Back to mud integrated website</A>\n" "</p>\n<H2><FONT COLOR=\"#BFBFBF\">%s Mudstats</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"http://www.dawnoftime.org/\">Go to the official Dawn of Time website</A>\n" " <A HREF=\"/\">Back to mud integrated website</A>\n" ,websvr_generate_head_and_start_body("Mudstats") ,MUD_NAME ,buf); }else if (!str_cmp(w->requestedLocation,"/robots.txt")){ // robots.txt - to prevent webcrawlers indexing the online helps w->statusCode=200; w->contentType=CONTENT_TEXT_PLAIN; strcpy(w->outBodyBuf, "User-agent: *\nDisallow: /help/\n"); w->outBodyLen=str_len(w->outBodyBuf); }else if (!str_cmp(w->requestedLocation,"/gamesettings")){ w->statusCode=200; buf=getwebGameSettings(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s GAME SETTINGS</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("GameSettings") ,MUD_NAME, buf); }else if (!str_cmp(w->requestedLocation,"/wizlist")){ w->statusCode=200; buf=getwebWizlist(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s WIZLIST</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Wizlist") ,MUD_NAME, buf); }else if (!str_prefix("/help/",w->requestedLocation) || !str_cmp("/help",w->requestedLocation)) { webHelpHits++; char entry_buf[10000]; char entry_buf2[10050]; char *webhelp_buf=(char*)malloc(128000);// temporary storage allocated buf=webhelp_buf; w->statusCode=200; if(w->requestedLocation[5]=='/'){ convertColour( getwebHelp( w->requestedLocation+6, entry_buf, 10000),buf, CT_HTML, true); }else{ convertColour( getwebHelp( w->requestedLocation+5, entry_buf, 10000),buf, CT_HTML, true); } sprintf(entry_buf2, "WebHelp - %s", entry_buf); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<H2><FONT COLOR=\"#BFBFBF\">%s WebHelp </H2><H3>- %s</H3></FONT>" #ifdef VALIDATE_HTML "<TT><FONT>%s</FONT></TT>" #else "<PRE><FONT>%s</PRE>" #endif ,websvr_generate_head_and_start_body(entry_buf2) , MUD_NAME, entry_buf, buf); free(webhelp_buf); // temporary storage freed }else if (!str_cmp(w->requestedLocation,"/classstats")){ w->statusCode=200; buf=getwebClassStats(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s CLASS STATS</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Class Stats") ,MUD_NAME,buf); }else if (!str_cmp(w->requestedLocation,"/racestats")){ w->statusCode=200; buf=getwebRaceStats(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s RACE STATS</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Race Stats") ,MUD_NAME,buf); }else if (!GAMESETTING5(GAMESET5_RACEINFO_DISABLED_IN_WEBSERVER) && !str_cmp(w->requestedLocation,"/raceinfo")) { w->statusCode=200; buf=getwebRaceInfo(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s Race Info</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Race Info") ,MUD_NAME,buf); }else if (!GAMESETTING5(GAMESET5_CLASSINFO_DISABLED_IN_WEBSERVER) && (!str_prefix("/classinfo/",w->requestedLocation) || !str_cmp("/classinfo",w->requestedLocation))) { w->statusCode=200; if(w->requestedLocation[10]=='/'){ buf=getwebClassInfo(w->requestedLocation+11); }else{ buf=getwebClassInfo(""); } w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s Race Info</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Class Info") ,MUD_NAME,buf); }else if (!str_cmp(w->requestedLocation,"/alignstats")){ w->statusCode=200; buf=getwebAlignStats(); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<A HREF=\"/\">Back to root</A><BR>\n" "<H2><FONT COLOR=\"#BFBFBF\">%s ALIGNMENT MATRIX</FONT></H2>" #ifdef VALIDATE_HTML "\n<TT><FONT>%s</FONT></TT>" #else "\n<PRE><FONT>%s</PRE>" #endif "<p><A HREF=\"/\">Back to root</A>" ,websvr_generate_head_and_start_body("Active Player Alignment Matrix") ,MUD_NAME,buf); }else if (!str_cmp(w->requestedLocation,"/immwho")){ int immweb_level=laston_web_level(w->username, w->password); // laston_web_level() returns the level/trust of the imm if the password is correct // returns -1 if it is an imm name, but no webpass has been set // otherwise returns 0 if incorrect or not imm if (immweb_level>0){ w->statusCode=200; buf=getwebWho(immweb_level); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "\n<PRE><FONT>%s</PRE>" "<p><A HREF=\"/\">[Back to root]</A> <A HREF=\"/sockets\">[sockets]</A> <A HREF=\"/mudstats\">[mudstats]</A>" ,websvr_generate_head_and_start_body("Immortal Wholist") ,buf); }else if (immweb_level==-1){ w->statusCode=200; buf=process_colour("`WYou haven't set a webpassword for your character,`1" "Use the webpass command within the game to do this.", NULL); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<PRE><FONT>%s</PRE>" "<p><A HREF=\"/\">[Back to root]</A> <A HREF=\"/sockets\">[sockets]</A> <A HREF=\"/mudstats\">[mudstats]</A>" ,websvr_generate_head_and_start_body("web password setting required!") ,buf); }else{ w->statusCode=401; logWeb("401 - Authorisation denied"); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "<html><head><title>Incorrect username/password</title></head>\n" "<body><h1>Incorrect username/password</h1>\n" "<p>You got the username or password incorrect.\n" "<p><A HREF=\"/\">Back to root</A>"); } }else if (!str_cmp(w->requestedLocation,"/lastonimm")){ int immweb_level=laston_web_level(w->username, w->password); // laston_web_level() returns the level/trust of the imm if the password is correct // returns -1 if it is an imm name, but no webpass has been set // otherwise returns 0 if incorrect or not imm if (immweb_level>0){ w->statusCode=200; buf=getwebLastonImm(immweb_level); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "\n<PRE><FONT>%s</PRE>" "<p><A HREF=\"/\">[Back to root]</A> <A HREF=\"/sockets\">[sockets]</A> <A HREF=\"/immwho\">[immwho]</A> <A HREF=\"/mudstats\">[mudstats]</A>" ,websvr_generate_head_and_start_body("Immortal Laston") ,buf); }else{ websvr_handle_insufficient_immweb_level(w, buf, immweb_level); } }else if (!str_cmp(w->requestedLocation,"/sockets")){ int immweb_level=laston_web_level(w->username, w->password); // laston_web_level() returns the level/trust of the imm if the password is correct // returns -1 if it is an imm name, but no webpass has been set // otherwise returns 0 if incorrect or not imm if (immweb_level>0){ w->statusCode=200; buf=getwebSockets(immweb_level); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<FONT COLOR=\"#BFBFBF\">" "\n<PRE><FONT>%s</PRE>" "<p><A HREF=\"/\">[Back to root]</A> <A HREF=\"/immwho\">[immwho]</A> <A HREF=\"/mudstats\">[mudstats]</A>" ,websvr_generate_head_and_start_body("Immortal Sockets") ,buf); }else{ websvr_handle_insufficient_immweb_level(w, buf, immweb_level); } }else if (!str_cmp(w->requestedLocation,"/")){ w->statusCode=200; w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "%s" // doctype, header, and start of body tag "<H2 ALIGN=\"CENTER\">%s Mud Integrated Webserver</FONT></H2>" "<P ALIGN=\"CENTER\"><A HREF=\"/who\">Who list</A><BR>" "<P ALIGN=\"CENTER\"><A HREF=\"/help\">WebHelp</A><BR>" "<P ALIGN=\"CENTER\"><A HREF=\"/areas\">Areas</A><BR>" "<P ALIGN=\"CENTER\"><A HREF=\"/mudstats\">Mudstats</A><BR>" "<A HREF=\"/wizlist\">The Wizlist</A><BR>" "<A HREF=\"/classstats\">Active Player Class Stats</A><BR>" "<A HREF=\"/racestats\">Active Player Race Stats</A><BR>" "%s" // raceinfo - optional with gamesetting "%s" // classinfo - optional with gamesetting "<A HREF=\"/alignstats\">Active Player Alignment Matrix</A><BR>" "<A HREF=\"/gamesettings\">Game Settings</A><BR>" "<A HREF=\"/immwho\">Immortal version of the who list - immonly</A><BR>" "<A HREF=\"/lastonimm\">List of imms recently on - immonly</A><BR>" "<A HREF=\"/sockets\">Connections - immonly</A></P>" "<P ALIGN=\"CENTER\"><A HREF=\"/locked\">Reset password</A><BR>" "<P ALIGN=\"CENTER\"><A HREF=\"http://www.dawnoftime.org/\">Go to the official Dawn of Time website</A></P>" "</P></BODY>" "</HTML>" ,websvr_generate_head_and_start_body(MUD_NAME) , MUD_NAME, GAMESETTING5(GAMESET5_RACEINFO_DISABLED_IN_WEBSERVER)?"":"<A HREF=\"/raceinfo\">Race Info</A><BR>", GAMESETTING5(GAMESET5_CLASSINFO_DISABLED_IN_WEBSERVER)?"":"<A HREF=\"/classinfo\">Class Info</A><BR>"); }else if (!str_cmp(w->requestedLocation,"/locked")){ w->statusCode=401; logWeb("401 - Authorisation denied"); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "<html><head><title>Incorrect username/password</title></head>\n" "<body><h1>Incorrect username/password</h1>\n" "<p>You got the username/password incorrect.\n" "<p>Try username=user and password=password at <A HREF=\"/locked2\">locked2</A> for a test.\n" "<p><A HREF=\"/\">Back to root</A>"); }else if (!str_cmp(w->requestedLocation,"/locked2")){ if (!strcmp(w->username,"user") && !strcmp(w->password,"password")){ w->statusCode=200; w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "<html><head><title>Access granted!</title></head>\n" "<body><h1>Access granted!</h1>\n" "<p>You got the password etc correct.\n" "<p>Click on <A HREF=\"/locked\">locked</A> to be denied.\n" "<p><A HREF=\"/\">Back to root</A>"); }else{ w->statusCode=401; logWeb("401 - Authorisation denied"); w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "<html><head><title>Incorrect username/password</title></head>\n" "<body><h1>Incorrect username/password</h1>\n" "<p>You got the username/password incorrect.\n" "<p>try username=user and password=password\n" "<p><A HREF=\"/\">Back to root</A>"); } }else{ w->statusCode=200; w->outBodyLen=snprintf(w->outBodyBuf,WEBOUT_BODY_BUF, "<html><head><title>Not found</title></head>\n" "<body><h1>couldn't find the url you wanted</h1>\n" "<p><A HREF=\"/\">Back to root</A>"); } if(w->outBodyLen<0){ logWebf("Body %d bytes!!! - invalid length, attempting to repair!", w->outBodyLen); if(str_len(buf)>WEBOUT_BODY_BUF-10){ w->outBodyBuf[WEBOUT_BODY_BUF-10]='\0'; w->outBodyLen=WEBOUT_BODY_BUF-9; }else{ w->outBodyLen=snprintf(w->outBodyBuf, WEBOUT_BODY_BUF, "an error occured while processing this webpage, please report this to the admin!"); w->statusCode=500; // Internal Error } } logWebf("Making header - Body %d bytes", w->outBodyLen); createHeader(w); } /**************************************************************************/ void processPOST(web_request_data *w){ webHits++; if (!str_cmp(w->requestedLocation,"/")){ w->statusCode=200; w->outBodyLen=sprintf(w->outBodyBuf, "<HTML>" "<HEAD>" "<TITLE>%s</TITLE>" "</HEAD>" "<BODY LINK=\"#0000ff\" VLINK=\"#800080\">" "<H2 ALIGN=\"CENTER\">%s Mud Integrated Webserver</FONT></H2>" "<P ALIGN=\"CENTER\"><A HREF=\"http://www.dawnoftime.org/\">Go to the official Dawn of Time website</A></P>" "<P ALIGN=\"CENTER\"><A HREF=\"/who\">Who list</A><BR>" "<P ALIGN=\"CENTER\"><A HREF=\"/mudstats\">Mudstats</A><BR>" "<A HREF=\"/wizlist\">The wizlist</A><BR>" "<A HREF=\"/immwho\">Immortal version of the who list</A><BR>" "<A HREF=\"/sockets\">Connections - immonly</A></P>" "<P ALIGN=\"CENTER\"><A HREF=\"/locked\">Reset password</A><BR>" "</P></BODY>" "</HTML>", MUD_NAME, MUD_NAME); }else if (!str_cmp(w->requestedLocation,"/locked2")){ if (!strcmp(w->username,"user") && !strcmp(w->password,"password")){ w->statusCode=200; w->outBodyLen=sprintf(w->outBodyBuf, "<html><head><title>Access granted!</title></head>\n" "<body><h1>Access granted!</h1>\n" "<p>You got the password etc correct.\n" "<p><A HREF=\"/\">Back to root</A>"); }else{ w->statusCode=401; logWeb("401 - Authorisation denied"); w->outBodyLen=sprintf(w->outBodyBuf, "<html><head><title>Incorrect username/password</title></head>\n" "<body><h1>Incorrect username/password</h1>\n" "<p>You got the username/password incorrect.\n" "<p>try username=user and password=password\n" "<p><A HREF=\"/\">Back to root</A>"); } }else{ w->statusCode=200; w->outBodyLen=sprintf(w->outBodyBuf, "<html><head><title>Not found</title></head>\n" "<body><h1>couldn't find the url you wanted</h1>\n" "<p><A HREF=\"/\">Back to root</A>"); } logWebf("making header"); createHeader(w); } /**************************************************************************/ void processWebRequest(connection_data *c){ web_request_data *w=c->web_request; char loguname[100]; logWebf("processWebRequest"); logWebf("REQUEST: %d LOCATION: '%s'", w->requestType, w->requestedLocation); if (w->username && str_len(w->username)>0){ strncpy(loguname, w->username, 99); loguname[99]='\0'; logWebf("USERNAME: '%s'", w->username); // logWebf("PASSWORD: '%s'", w->password); }else{ loguname[0]='-'; loguname[1]='\0'; } switch (w->requestType){ case GET: processGET(w); break; case HEAD: badRequestFormat(w, "HEAD - Unsupported method"); break; case POST: processPOST(w); // badRequestFormat(w, "POST - Unsupported method"); break; case UNDEFINED: default: badRequestFormat(w, "Undefined method"); break; } // Web log stuff - can use with webalizer (http://www.mrunix.net/webalizer) // and other website stat generating software if(!(game_settings->flags2 & GAMESET2_NO_WEBLOG)) { char logdate[100]; time_t ltime; time( <ime ); #ifdef WIN32 // VC++6 compiler doesn't support %z, doesn't really matter // hard coded it to NZDT :) strftime(logdate, 100, "[%d/%b/%Y:%H:%M:%S +1300]", localtime( <ime)); #else strftime(logdate, 100, "[%d/%b/%Y:%H:%M:%S %z]", localtime( <ime)); #endif char weblogbuf[4096]; sprintf(weblogbuf,"%s - %s %s \"%s\" %d %d \"%s\" \"%s\"", c->remote_ip, // 50 bytes loguname, // 100 bytes logdate, // 100 bytes w->firstline, // 1024 max w->statusCode, // 3 bytes w->outBodyLen, // 6 bytes max w->referer, // 1024 max w->user_agent); // 1024 max char weblogfname[30]; sprintf(weblogfname, "weblog%d.txt", c->local_port); append_string_to_file(weblogfname, weblogbuf, true); } } /**************************************************************************/ void filterWebRequest(connection_data *c){ web_request_data *w=c->web_request; char *pCRLF, *pHTTP, *pCRLFCRLF; pCRLF=NULL; if (w->receivingHeader){ // dont filter it if there isn't enough to consist a valid // html header request if (w->inHeaderLen<2) return; // next check if the first line has been receieved // an HTML request must finish with CRLF as per RFC 1945 pCRLF = strstr( w->inHeaderBuf, "\r\n" ); if (!pCRLF){ return; } // check if it is a HTTP/0.9 request // i.e. no "HTTP/" before the first CRLF pHTTP = strstr( w->inHeaderBuf, "HTTP/" ); if (!pHTTP || pHTTP>pCRLF){ // it must be a Simple-Request (HTTP/0.9) logWebf("HTTP/0.9 request from %s request:%s", c->remote_ip,w->inHeaderBuf); processHTTP0_9Request(w); return; } // PRE: Request is HTTP/1.0 or higher // next check if the header has been completed // If so the end of the header is marked with CRLFCRLF pCRLFCRLF = strstr( w->inHeaderBuf, "\r\n\r\n" ); if (pCRLFCRLF){ *(pCRLFCRLF+2)='\0'; // logWebf ("%d %d", (int)*pCRLFCRLF, (int) *(pCRLFCRLF+4)); processWebHeader(w); // now if we have a content length if (w->contentLength){ strcpy(w->inBodyBuf,pCRLFCRLF+4); w->inBodyLen = str_len(w->inBodyBuf); // pHTTP = strstr( w->inHeaderBuf, "contentlength" ); // strcpy(w->inBodyBuf,pCRLFCRLF+4); } }else{ // isn't the end of the header yet // do nothing really } } // read the in body if (!w->receivingHeader && (w->inBodyLen>=w->contentLength)){ } //Content-Length: logWebf("HTTP/1.0 request from %s",c->remote_ip); // logf("%s", w->inHeaderBuf); // filter it in here { char *pStr, *pLine; char workingline[WEBIN_HEADER_BUF+100]; char method[21]; // check request isn't too long if (pCRLF>w->inHeaderBuf+WEBIN_HEADER_BUF){ badRequestFormat(w, "Request-Line too long"); return; } strncpy(workingline, w->inHeaderBuf, pCRLF-w->inHeaderBuf); workingline[pCRLF-w->inHeaderBuf]='\0'; pLine=workingline; // copy parameters, checking the // length of each command as we go strncpy(w->firstline, workingline, 1024); w->firstline[1023]='\0'; // extract the METHOD pStr=strstr( pLine, " "); if (!pStr){ badRequestFormat(w, "Request-Line invalid"); return; } if (pStr-pLine>20){ badRequestFormat(w, "method in Request-Line too long"); return; } strncpy(method, pLine, pStr-pLine); method[pStr-pLine]='\0'; pLine=pStr+1; // extract the LOCATION pStr=strstr( pLine, " "); if (!pStr){ badRequestFormat(w, "Request-Line invalid"); return; } if (pStr-pLine>MAXLOCATION){ badRequestFormat(w, "Request-URI (location) in Request-Line too long"); return; } strncpy(w->requestedLocation, pLine, pStr-pLine); w->requestedLocation[pStr-pLine]='\0'; pLine=pStr+1; // extract the VERSION if (strstr( pLine, "HTTP/" )==NULL) { badRequestFormat(w, "no HTTP version in Request-Line"); } if (str_len(pLine)>20){ badRequestFormat(w, "HTTP-Version in Request-Line too long"); return; } strcpy(w->version, pLine); // now identify the method if (!strcmp(method,"GET")){ w->requestType=GET; }else if (!strcmp(method,"HEAD")){ w->requestType=HEAD; }else if (!strcmp(method,"POST")){ w->requestType=POST; }else{ w->requestType=UNDEFINED; } // check for a "Referer:" header pLine=strstr( w->inHeaderBuf, "Referer:"); if(pLine){ pLine+=str_len("Referer:"); while(is_space(*pLine)){ pLine++; } // we have an Referer - extract it pStr=strstr( pLine, "\r\n"); if (pStr){ strncpy(workingline, pLine, pStr-pLine); workingline[pStr-pLine]='\0'; strncpy(w->referer,workingline, 1024); w->referer[1023]='\0'; }else{ strcpy(w->referer,"-"); } }else{ strcpy(w->referer,"-"); } // check for a "User-Agent:" header pLine=strstr( w->inHeaderBuf, "User-Agent:"); if(pLine){ pLine+=str_len("User-Agent:"); while(is_space(*pLine)){ pLine++; } // we have an User-Agent - extract it pStr=strstr( pLine, "\r\n"); if (pStr){ strncpy(workingline, pLine, pStr-pLine); workingline[pStr-pLine]='\0'; strncpy(w->user_agent,workingline, 1024); w->user_agent[1023]='\0'; }else{ strcpy(w->user_agent,"-"); } }else{ strcpy(w->user_agent,"-"); } // check for an "Authorization:" header pLine=strstr( w->inHeaderBuf, "Authorization:"); if(pLine){ // we have an Authorization - process it pStr=strstr( pLine, "\r\n"); if (pStr){ strncpy(workingline, pLine, pStr-pLine); workingline[pStr-pLine]='\0'; pLine=workingline; //we have the authorization string in newline logWebf("Processing Authorization: '%s'",workingline); pStr=strstr( pLine, "Basic"); if (pStr){ pLine=pStr+6; if (str_len(pLine)>0 && str_len(pLine)<200){ char decoded[200]; char *d; d =decodeBase64(pLine); if (d){ sprintf(decoded,d); // now separate the username:password pLine=decoded; pStr=strstr( pLine, ":"); if (pStr){ if (pStr-pLine<MAXPASSLENGTH){ strncpy(w->username, pLine, pStr-pLine); w->username[pStr-pLine]='\0'; pLine=pStr+1; if (str_len(pLine)>MAXPASSLENGTH){ // not a valid password - so authuenticate invalid w->username[0]='\0'; }else{ strcpy(w->password, pLine); } } } } } }else{ logWeb("Authorization type not recognised"); } } } // filtering completed - now process the request processWebRequest(c); } return; } /**************************************************************************/ /**************************************************************************/