//***************************************************************************** // // webserver.c // // This is a module that opens an HTTP server on another port that allows // people to request various sorts of information. Useful for displaying online // who lists, and that sort of junk. // //***************************************************************************** #include "../mud.h" #include "../utils.h" #include "../inform.h" #include "../event.h" #include "webserver.h" //***************************************************************************** // local variables, datastructures, functions, and defines //***************************************************************************** // the socket for our server int web_control; // our list of descriptors connected LIST *web_descs = NULL; // what is the mapping from query:function ? HASHTABLE *query_table = NULL; // the datstructure for a web descriptor typedef struct web_socket { int control; struct sockaddr_in addr; char inbuf[MAX_INPUT_LEN]; int buf_len; } WEB_SOCKET; WEB_SOCKET *newWebSocket() { WEB_SOCKET *sock = calloc(1, sizeof(WEB_SOCKET)); return sock; } void deleteWebSocket(WEB_SOCKET *sock) { free(sock); } void webSocketClose(WEB_SOCKET *sock) { close(sock->control); } // // convert all of the color codes and ascii characters to their // HTML equivilants. void bufferASCIIHTML(BUFFER *buf) { bufferReplace(buf, "\r", "", TRUE); bufferReplace(buf, "\n", "<br>", TRUE); bufferReplace(buf, " ", " ", TRUE); bufferReplace(buf, "{n", "<font color=\"green\">", TRUE); bufferReplace(buf, "{g", "<font color=\"green\">", TRUE); bufferReplace(buf, "{w", "<font color=\"silver\">", TRUE); bufferReplace(buf, "{p", "<font color=\"purple\">", TRUE); bufferReplace(buf, "{b", "<font color=\"navy\">", TRUE); bufferReplace(buf, "{y", "<font color=\"olive\">", TRUE); bufferReplace(buf, "{r", "<font color=\"maroon\">", TRUE); bufferReplace(buf, "{c", "<font color=\"teal\">", TRUE); bufferReplace(buf, "{d", "<font color=\"black\">", TRUE); bufferReplace(buf, "{G", "<font color=\"lime\">", TRUE); bufferReplace(buf, "{W", "<font color=\"white\">", TRUE); bufferReplace(buf, "{P", "<font color=\"magenta\">", TRUE); bufferReplace(buf, "{B", "<font color=\"blue\">", TRUE); bufferReplace(buf, "{Y", "<font color=\"yellow\">", TRUE); bufferReplace(buf, "{R", "<font color=\"red\">", TRUE); bufferReplace(buf, "{C", "<font color=\"aqua\">", TRUE); bufferReplace(buf, "{D", "<font color=\"grey\">", TRUE); } // // used by webSocketHandle. Replaces one char with another in the buf. // Returns how many replacements were made int replace_char(char *buf, char from, char to) { int replacements = 0; for(;*buf != '\0'; buf++) { if(*buf == from) { *buf = to; replacements++; } } return replacements; } // // handle whatever request the socket is making void webSocketHandle(WEB_SOCKET *sock) { static char get[SMALL_BUFFER]; static char key[SMALL_BUFFER]; char *argstr = NULL; BUFFER *buf = newBuffer(MAX_BUFFER); HASHTABLE *args = newHashtable(); // clear everything *get = *key = '\0'; // parse our key two_args(sock->inbuf, get, key); // replace the arg separator with a space, and then one_arg out the arguments if(replace_char(key, '?', ' ')) argstr = strdup(one_arg(key+1, key)); else one_arg(key+1, key); // parse out our argument string if(argstr != NULL) { char one_pair[SMALL_BUFFER]; char one_key[SMALL_BUFFER]; char one_val[SMALL_BUFFER]; char *leftover = argstr; // separate all of our key:val pairs by spaces so we can one_arg them replace_char(argstr, '&', ' '); do { // clear our buffers *one_pair = *one_key = *one_val = '\0'; // parse out a pair, and replace the assigner // with a space so we can two_args them apart leftover = one_arg(leftover, one_pair); replace_char(one_pair, '=', ' '); two_args(one_pair, one_key, one_val); // see if we need to put in the new key:val pair if(*one_key && *one_val && !hashIn(args, one_key)) hashPut(args, one_key, strdup(one_val)); } while(*leftover != '\0'); } // find our function BUFFER *(* func)(HASHTABLE *args) = hashGet(query_table, key); BUFFER *content = NULL; // call it if it exists if(func == NULL || (content = func(args)) == NULL) bprintf(buf, "<html><body>Your request for %s was not found</body></html>", key); else { // replace all of the colors and returns in the buf bufferASCIIHTML(content); // generate the output, with all of its required tags bprintf(buf, "<html><body bgcolor=\"black\" text=\"green\">" "<font face=\"courier\">" "%s" "</font>" "</body></html>", bufferString(content)); } // if we had content, delete it now if(content != NULL) deleteBuffer(content); // send out the buf contents send(sock->control, bufferString(buf), strlen(bufferString(buf)), 0); // clean up our mess deleteBuffer(buf); if(argstr) free(argstr); if(hashSize(args) > 0) { const char *h_key = NULL; char *h_val = NULL; HASH_ITERATOR *arg_i = newHashIterator(args); ITERATE_HASH(h_key, h_val, arg_i) { free(h_val); } deleteHashIterator(arg_i); } deleteHashtable(args); } // // A wrapper function for build_who to be usable in the web server BUFFER *build_who_html(HASHTABLE *args) { return build_who(); } // // the main loop for our web server void webserver_loop(void *owner, void *data, char *arg) { WEB_SOCKET *conn = NULL; struct timeval tv = { 0, 0 }; // we don't wait for any action. fd_set read_fd; // get our sets all done up FD_ZERO(&read_fd); FD_SET(web_control, &read_fd); // check to see if something happens select(FD_SETSIZE, &read_fd, NULL, NULL, &tv); // check for a new connection if(FD_ISSET(web_control, &read_fd)) { conn = newWebSocket(); unsigned int socksize; // try to accept the connection if((conn->control = accept(web_control, (struct sockaddr *)&conn->addr, &socksize)) > 0) { listQueue(web_descs, conn); FD_SET(conn->control, &read_fd); } } // do input handling LIST_ITERATOR *conn_i = newListIterator(web_descs); ITERATE_LIST(conn, conn_i) { if(FD_ISSET(conn->control, &read_fd)) { int in_len = read(conn->control, conn->inbuf + conn->buf_len, MAX_INPUT_LEN - conn->buf_len - 1); if(in_len > 0) { conn->buf_len += in_len; conn->inbuf[conn->buf_len] = '\0'; } else if(in_len < 0) { webSocketClose(conn); listRemove(web_descs, conn); deleteWebSocket(conn); } } } deleteListIterator(conn_i); // do output handling conn_i = newListIterator(web_descs); ITERATE_LIST(conn, conn_i) { // which version are we dealing with, and do we have a request terminator? // If we haven't gotten the terminator yet, don't handle or close the socket if( ( strstr(conn->inbuf, "HTTP/1.") && strstr(conn->inbuf, "\r\n\r\n")) || (!strstr(conn->inbuf, "HTTP/1.") && strstr(conn->inbuf, "\n"))) { webSocketHandle(conn); webSocketClose(conn); listRemove(web_descs, conn); deleteWebSocket(conn); } } deleteListIterator(conn_i); } //***************************************************************************** // implementation of webserver.h //***************************************************************************** void init_webserver(void) { struct sockaddr_in my_addr; int sockfd, reuse = 1; // grab a socket sockfd = socket(AF_INET, SOCK_STREAM, 0); // set its values my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = INADDR_ANY; my_addr.sin_port = htons(WEB_PORT); // fix thread problems? if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) == -1) { log_string("Webserver error in setsockopt()"); exit(1); } // bind the port bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)); // start listening for connections listen(sockfd, 3); // set the socket control web_control = sockfd; // set up our list of connected sockets, and get the updater rolling web_descs = newList(); query_table = newHashtable(); start_update(NULL, 0.1 SECOND, webserver_loop, NULL, NULL, NULL); // set up our basic queries add_query("who", build_who_html); } void finalize_webserver(void) { WEB_SOCKET *conn = NULL; LIST_ITERATOR *conn_i = newListIterator(web_descs); ITERATE_LIST(conn, conn_i) { close(conn->control); } deleteListIterator(conn_i); close(web_control); } void add_query(const char *key, BUFFER *(* func)(HASHTABLE *args)) { hashPut(query_table, key, func); }