// webserver (simple times) class web_data; int web_control = 0; int web_desc; #define WEB_PORT 9697 Lexi::Listweb_list; const char ENDREQUEST[5] = { 13, 10, 13, 10, 0 }; /* (CRLFCRLF) */ #define START_REQUEST "HTTP/1." #define GET_REQUEST "GET" // Yay Classing! class web_data() { public: web_data() { descriptor = 0; inbuf[0] = '\0'; incomm[0] = '\0'; init_at = current_time; host = NULL; temporary = NULL; base_url = NULL; chunk = NULL; after = NULL; } ~web_data() { web_list.remove_one(this); close(descriptor); free_string(host); free_string(temporary); free_string(base_url); free_string(chunk); free_string(after); } private: int descriptor; char inbuf [4 * MAX_INPUT_LENGTH]; char incomm [MAX_INPUT_LENGTH]; const char * host; const char * temporary; const char * base_url; const char * chunk; const char * after; time_t init_at; public: void Sendf(const char *msg, ...) { va_list ap; va_start(ap, msg); char buf[MSL]; vsnprintf(buf, sizeof(buf), msg, ap); va_end(ap); send(descriptor, buf, strlen(buf), 0); return; } bool read_from_web(); void manage_results(); void process_request(); const char *get_incomm() { return incomm; } const char *get_chunk() { return chunk; } const char *get_after() { return after; } const char *get_host() { return host; } const char *get_base() { return base_url; } void header(const char *title) { Sendf("%s\n\r",title); Sendf("%s

\n\r", title); } void footer(const char *footer) { Sendf(""); } void unknown_page() { header("404 - Page not found!"); Sendf("We are sorry, but the page you are looking for doesn't exist. Nice try!"); footer(""); } void format_request () { char temp[MSL]; temporary = str_dup (first_arg (incomm, temp, false)); first_arg (temporary, temp, false); if (temp[0] == '/') chunk = str_dup (&temp[1]); else chunk = str_dup (temp); after = get_next (chunk, base_url); return; } const char *get_next (const char *path, char *base) { static char *buf[5]; static int i; int x; if (NullString (chunk)) return chunk; ++i, i %= 5; buf[i] = strchr (path, '/'); base[0] = NUL; if (!NullString (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]; } }; bool lastcolor = FALSE; #define SET_WWW_FONT(buf, color) \ do \ { \ strcpy(buf, ""); \ 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, "", MSL); else out[0] = '\0'; switch (type) { case '\0': break; case ' ': strncpy(code, " ", MIL); break; default: case '`': SET_WWW_FONT(code, "silver"); break; case '4': SET_WWW_FONT(code, "navy"); break; case '6': SET_WWW_FONT(code, "teal"); break; case '2': SET_WWW_FONT(code, "green"); break; case '5': SET_WWW_FONT(code, "purple"); break; case '1': SET_WWW_FONT(code, "maroon"); break; case '7': SET_WWW_FONT(code, "silver"); break; case '3': SET_WWW_FONT(code, "olive"); break; case '$': SET_WWW_FONT(code, "blue"); break; case '^': SET_WWW_FONT(code, "cyan"); break; case '@': SET_WWW_FONT(code, "lime"); break; case '%': SET_WWW_FONT(code, "magenta"); break; case '!': SET_WWW_FONT(code, "red"); break; case '&': SET_WWW_FONT(code, "white"); break; case '#': SET_WWW_FONT(code, "yellow"); break; case '8': SET_WWW_FONT(code, "black"); break; case '*': 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 '{': 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 = ""; lastcolor = FALSE; for (point = txt; *point; point++) { if (*point == '{') { point++; if (*point == '\0') point--; else skip = html_colour(*point, buffer); while (skip-- > 0) ++buffer; 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; } void send_who(web_data *web) { int counter = 0; header("Who is online CombatMud"); FOREACH(Lexi::List, player_list, player_iter) { CHAR_DATA *ch = (*player_iter); if(ch->invis_level >0) continue; // simple. web->Sendf("
[%d]<%s> %s %s", ch->level, class_table[ch->iclass].name, ch->name, ch->title ? ch->title : "" ); counter++; } web->Sendf("There are %d visible players on CombatMud!\n\r", counter); footer(""); return; } // Sending helpfile! void send_help(web_data *web) { int count = 0; int pos = 0; HELP_DATA *pHelp; if(NullString(web->get_after()) { web->header("Help Files"); web->Sendf(""); for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next) { count++; if (pHelp->level == -1 || pHelp->level == 0) { const char *temp; char wordkey[MSL]; temp = pHelp->keyword; while (!NullString (temp)) { wordkey[0] = '\0'; temp = one_argument (temp, wordkey); web->Sendf("%s",(pos == 0) ? "" : "", HTTP_URL (HELP_URL_PREFIX "/%d", count), wordkey); if (++pos % 5 == 0) { web->Sendf(""); pos = 0; } } } } web->Sendf("
%s
"); web->Sendf(""); web->footer(""); } else { char temp[MIL]; pos = 0; for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next) { pos++; char buf[MSL]; sprintf (temp, "%d", pos); if (!str_cmp (web->get_after(), temp)) { html_colourconv(buf, fix_string(pHelp->text)); // correct colour! web->header(pHelp->keyword); web->Sendf(""); web->Sendf("", pHelp->level, pHelp->keyword); web->Sendf("",buf); web->Sendf("
[%d] %s
%s
"); web->Sendf(""); return; } } } web->header("Can't find help"); web->Sendf("

Can't find help %s

", web->get_after()); web->footer(""); return; } void web_data::process_request() { /* process request */ /* are we using HTTP/1.x? If so, write out header stuff.. */ if(!strstr(incomm, GET_REQUEST)) { Sendf("HTTP/1.0 501 Not Implemented"); return; } format_request(); if(strstr(incomm, START_REQUEST)) { Sendf("HTTP/1.0 200 OK\n"); Sendf("Content-type: text/html\n\n"); } // handle our results... To be tabled.. Someday.. if(strstr(chunk, "/online")) send_who(this); else if(strstr(incomm, "/help")) send_help(this); else unknown_page(); } bool web_data::read_from_web(){ int iStart; web_data *d = this; if ( d->incomm[0] != '\0' ) return TRUE; iStart = strlen(d->inbuf); if ( iStart >= sizeof(d->inbuf) - 10 ) { log_string( "%s input overflow in web processor.!", d->host ); return FALSE; } for ( ; ; ) { int nRead; nRead = read( d->descriptor, d->inbuf + iStart, sizeof(d->inbuf) - 10 - iStart ); if ( nRead > 0 ) { iStart += nRead; if ( d->inbuf[iStart-1] == '\n' || d->inbuf[iStart-1] == '\r' ) break; } else if ( nRead == 0 ) { log_string( "EOF encountered on read." ); return FALSE; } else if ( errno == EWOULDBLOCK ) break; else { perror( "Read_from_descriptor" ); return FALSE; } } d->inbuf[iStart] = '\0'; return TRUE; } void web_data::manage_results() { int i, j, k; unsigned char *p; if ( incomm[0] != '\0' ) // already processing the web return; /* * Look for at least one new line. */ for ( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ ) { if ( d->inbuf[i] == '\0' ) return; } /* * Canonical input processing. */ for ( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ ) { if ( k >= MAX_INPUT_LENGTH - 2 ) { write_to_descriptor( d->descriptor, "Line too long.\n\r", 0 ); write_to_descriptor( d, "Line too long.\n\r", 0 ); // End MCCP /* skip the rest of the line */ for ( ; d->inbuf[i] != '\0'; i++ ) { if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' ) break; } d->inbuf[i] = '\n'; d->inbuf[i+1] = '\0'; break; } if ( d->inbuf[i] == '\b' && k > 0 ) --k; else if ( isascii(d->inbuf[i]) && isprint(d->inbuf[i]) ) d->incomm[k++] = d->inbuf[i]; } /* * Finish off the line. */ if ( k == 0 ) d->incomm[k++] = ' '; d->incomm[k] = '\0'; while ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' ) i++; for ( j = 0; ( d->inbuf[j] = d->inbuf[i+j] ) != '\0'; j++ ) ; } void web_new( int wc ) { char buf[MAX_STRING_LENGTH]; web_data *web; struct sockaddr_in sock; struct hostent *from; int desc; socklen_t size; size = sizeof(sock); getsockname( wc, (struct sockaddr *) &sock, &size ); if ( ( desc = accept( wc, (struct sockaddr *) &sock, &size) ) < 0 ) { perror( "web_new: accept" ); return; } #if !defined(FNDELAY) #define FNDELAY O_NDELAY #endif if ( fcntl( desc, F_SETFL, FNDELAY ) == -1 ) { perror( "web_new: fcntl: FNDELAY" ); return; } web = new web_data(); web->descriptor = desc; web_list.push_back(); size = sizeof(sock); if ( getpeername( desc, (struct sockaddr *) &sock, &size ) < 0 ) { perror( "New_descriptor: getpeername" ); dnew->host = str_dup( "(unknown)" ); } else { /* * Would be nice to use inet_ntoa here but it takes a struct arg, * which ain't very compatible between gcc and system libraries. */ int addr; addr = ntohl( sock.sin_addr.s_addr ); sprintf( buf, "%d.%d.%d.%d", ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF ); log_string("Web Descriptor: Sock.sinaddr: %s", buf ); from = gethostbyaddr( (char *) &sock.sin_addr, sizeof(sock.sin_addr), AF_INET ); web->host = str_dup( from ? from->h_name : buf ); } } // open the web-port! void webserver_start() { extern int init_socket(int ); web_control = init_socket(WEB_PORT); return; } // shutdown the webserver void webserver_shutdown() { close(web_control); // prevents connect attemps during shutdown. FOREACH(Lexi::List, web_list, web_iter) { web_data *web = (*web_iter); delete web; } return; } // process the webserver void webserver_loop() { static struct timeval null_time; struct timeval last_time; fd_set in_set; fd_set out_set; fd_set exc_set; FD_ZERO( &in_set ); FD_ZERO( &out_set ); FD_ZERO( &exc_set ); FD_SET( control, &in_set ); web_desc = web_control; FOREACH(Lexi::List, web_list, web_iter) { { web_data *web = (*web_iter); web_desc = UMAX( web_desc, d->descriptor ); FD_SET( d->descriptor, &in_set ); FD_SET( d->descriptor, &out_set ); FD_SET( d->descriptor, &exc_set ); } if ( select( web_desc+1, &in_set, &out_set, &exc_set, &null_time ) < 0 ) { perror( "Game_loop: select: poll" ); exit( 1 ); } if ( FD_ISSET( web_control , &in_set ) ) web_new( web_control ); /* remove broken sockets. */ FOREACH(Lexi::List, web_list, web_iter) { web_data *web = (*web_iter); if ( FD_ISSET( web->descriptor, &exc_set ) ) { FD_CLR( web->descriptor, &in_set ); FD_CLR( web->descriptor, &out_set ); delete web; continue; } // purge our timed out web-descriptors. if(web->init_at < current_time+400) { delete web; continue; } } /* Process input and fire out the data*/ FOREACH(Lexi::List, web_list, web_iter) { web_data *web = (*web_iter); if ( FD_ISSET( web->descriptor, &in_set ) ) { if ( !web->read_from_web( ) ) { FD_CLR( web->descriptor, &out_set ); delete web; continue; } } web->manage_results(); if(strstr(web->get_incomm(), START_REQUEST ) && strstr(web->get_incomm(), ENDREQUEST)) process_web(web); else if(!strstr(web->get_incomm(), START_REQUEST ) && strchr(web->get_incomm(), '\n')) /* HTTP/0.9 (no ver number) */ process_web(web); else { continue; /* Don't have full request yet! */ } // killinate what needs to be killinated. delete web; } return; }