X-Git-Url: https://secure.softndesign.org/git/?a=blobdiff_plain;f=http.c;h=fbd18ef87e67ba23e0ceabc2728e4d1c6d298e34;hb=7a559b2cdf3c9060af1645ae73420c4b91b19cf1;hp=288d7b09190be9b9e3e6646d1d172e733bb8a834;hpb=d0b0d52bd4c8816c538d7704a6bf95f3dad310b2;p=webserver.git diff --git a/http.c b/http.c index 288d7b0..fbd18ef 100644 --- a/http.c +++ b/http.c @@ -1,12 +1,425 @@ #include +#include #include +#include #include "debug.h" +#include "file.h" #include "http.h" -int processing (unsigned char *data, int len, unsigned char **pdata) { - *pdata = data; +#define BUFFER_SIZE 128 + +#define HTTP_VERSION "HTTP/1.0" +#define SERVER_NAME "Webserver/0.0.1" + +char *codes[15] = { + "200 OK", + "201 Created", + "202 Accepted", + "204 No Content", + "301 Moved Permanently", + "302 Moved Temporarily", + "304 Not Modified", + "400 Bad Request", + "401 Unauthorized", + "403 Forbidden", + "404 Not Found", + "500 Internal Server Error", + "501 Not Implemented", + "502 Bad Gateway", + "503 Service Unavailable"}; + +typedef enum { + c200 = 0, c201, c202, c204, + c301, c302, c304, c400, + c401, c403, c404, c500, + c501, c502, c503 +} code_t; + +typedef enum { + not_supported_e = 0, get_e, head_e, post_e +} method_t; + +typedef struct { + char *allow; + char *authorization; + char *content_encoding; + char *content_length; + char *content_type; + char *date; + char *expires; + char *from; + char *if_modified_since; + char *last_modified; + char *location; + char *pragma; + char *referer; + char *server; + char *user_agent; + char *www_authenticate; +} header_t; + +typedef struct { + char *ext; + char *type; + char *charset; +} mime_t; + +#define NB_MIMES 8 + +mime_t mimes[NB_MIMES] = { + {"js", "application/javascript", "iso-8859-1"}, + {"css", "text/css", "iso-8859-1"}, + {"htm", "text/html", "iso-8859-1"}, + {"html", "text/html", "iso-8859-1"}, + {"png", "image/png", NULL}, + {"jpeg", "image/jpeg", NULL}, + {"jpg", "image/jpeg", NULL}, + {"txt", "text/plain", "iso-8859-1"} +}; + +/* find mime type */ + +mime_t *find_mime_type (char *filename) +{ + /* find extention */ + char *ext = filename + strlen (filename); + while (--ext > filename) { + if (ext[-1] == '.') { + break; + } + } + if (ext == filename) { + return NULL; + } + + /* find mime */ + int i; + for (i = 0; i < NB_MIMES; i++) { + if (strcmp (ext, (mimes + i)->ext) == 0) { + return mimes + i; + } + } + + return NULL; +} + +/* print header values */ + +void print_header_values (header_t *header) +{ + printf ("Header values\n"); + if (header->allow) printf ("allow = '%s'\n", header->allow); + if (header->authorization) printf ("authorization = '%s'\n", header->authorization); + if (header->content_encoding) printf ("content_encoding = '%s'\n", header->content_encoding); + if (header->content_length) printf ("content_length = '%s'\n", header->content_length); + if (header->content_type) printf ("content_type = '%s'\n", header->content_type); + if (header->date) printf ("date = '%s'\n", header->date); + if (header->expires) printf ("expires = '%s'\n", header->expires); + if (header->from) printf ("from = '%s'\n", header->from); + if (header->if_modified_since) printf ("if_modified_since = '%s'\n", header->if_modified_since); + if (header->last_modified) printf ("last_modified = '%s'\n", header->last_modified); + if (header->location) printf ("location = '%s'\n", header->location); + if (header->pragma) printf ("pragma = '%s'\n", header->pragma); + if (header->referer) printf ("referer = '%s'\n", header->referer); + if (header->server) printf ("server = '%s'\n", header->server); + if (header->user_agent) printf ("user_agent = '%s'\n", header->user_agent); + if (header->www_authenticate) printf ("www_authenticate = '%s'\n", header->www_authenticate); +} + +/* find sequence*/ + +char *find_sequence (char *data, int len, char *seq, char **pdata) +{ + + int size = strlen (seq); + + int i; + for (i = 0; i < len - size + 1; i++) { + if (strncmp (data + i, seq, size) == 0) { + data[i] = 0; + if (pdata != NULL) { + *pdata = data + i + size; + } + return data; + } + } + + return NULL; +} + +/* response entity */ + +int add_line (char **buffer, char *str) +{ + VERBOSE (DEBUG, PRINT ("add line: %s\n", str)); + int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3; + VERBOSE (DEBUG, PRINT ("len: %d\n", len)); + if (*buffer) { + *buffer = (char *)realloc (*buffer, len); + } else { + *buffer = (char *)calloc (len, 1); + } + strcat (*buffer, str); + strcat (*buffer, "\r\n"); + return len; +} + +int add_status_line (char **buffer, code_t code) +{ + char tmp[BUFFER_SIZE] = {0}; + + /* Status */ + sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]); + add_line (buffer, tmp); + + return strlen (*buffer); +} + +int add_general_header (char **buffer) +{ + char tmp[BUFFER_SIZE] = {0}; + + /* Date */ + time_t ts = time (NULL); + sprintf (tmp, "Date: %s", ctime (&ts)); + tmp[strlen (tmp) - 1] = 0; // remove last \n + add_line (buffer, tmp); + + /* Pragma */ + + return strlen (*buffer); +} + +int add_response_header (char **buffer, char *uri) +{ + char tmp[BUFFER_SIZE] = {0}; + + /* Location */ + sprintf (tmp, "Location: %s", uri); + add_line (buffer, tmp); + + /* Server */ + sprintf (tmp, "Server: %s", SERVER_NAME); + add_line (buffer, tmp); + + /* WWW-Authentificate */ + + return strlen (*buffer); +} + +int add_entity (char **buffer, char *entity, int size, char *type, char *encoding) +{ + char tmp[BUFFER_SIZE] = {0}; + int len = strlen (*buffer); + + /* Allow */ + /* Expires */ + /* Last-Modified */ + + if (entity != NULL) { + + /* Content-Encoding */ + if (encoding != NULL) { + sprintf (tmp, "Content-Encoding: %s", encoding); + add_line (buffer, tmp); + } + + /* Content-Length */ + sprintf (tmp, "Content-Length: %d", size); + add_line (buffer, tmp); + + /* Content-Type */ + sprintf (tmp, "Content-Type: %s", type); + add_line (buffer, tmp); + + add_line (buffer, ""); + + /* Entity */ + len = strlen (*buffer); + *buffer = realloc (*buffer, len + size); + memcpy (*buffer + len, entity, size); + len += size; + } + + return len; +} + +/* error 400 */ + +int error_400 (char **buffer) +{ + add_status_line (buffer, c400); + add_general_header (buffer); + add_response_header (buffer, NULL); + + char *response = "Error 400

Bad Request

"; + return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1"); +} + +/* error 404 */ + +int error_404 (char **buffer, char *uri) +{ + add_status_line (buffer, c404); + add_general_header (buffer); + add_response_header (buffer, uri); + + char *response = "Error 404

File not found

"; + return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1"); +} + +/* generic response */ + +int generic_response (char **buffer, char *location, char *response, int size) +{ + int len = 0; + VERBOSE (DEBUG, PRINT ("add_status_line %d\n", len)); + len = add_status_line (buffer, c200); + VERBOSE (DEBUG, PRINT ("add_general_header %d\n", len)); + len = add_general_header (buffer); + VERBOSE (DEBUG, PRINT ("add_response_header %d\n", len)); + len = add_response_header (buffer, location); + mime_t *mime = find_mime_type (location); + + VERBOSE (DEBUG, PRINT ("add_entity %d\n", len)); + return add_entity (buffer, response, size, mime->type, mime->charset); +} + +/* trim string */ + +char *trim (char *str) +{ + if (str != NULL) { + while ((*str == ' ') || (*str == '\t')) { + str++; + } + } + return str; +} + +/* main HTTP processing */ + +int processing (char *data, int len, conf_t *conf, char **pdata) +{ + char *saved_data = data; + char location[BUFFER_SIZE] = {0}; + VERBOSE (DEBUG, PRINT ("Start processing\n")); + + /* check method */ + char *line = find_sequence (data, len + data - saved_data, "\r\n", &data); + if (line == NULL) { + VERBOSE (WARNING, PRINT ("Unknown received data\n")); + if (pdata != NULL) { + *pdata = NULL; + } + return 0; + } + VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line)); + + char *method = strtok (line, " "); + char *uri = strtok (NULL, " "); + char *version = strtok (NULL, " "); + method_t type = not_supported_e; + if (strcmp (method, "GET") == 0) { + type = get_e; + } else if (strcmp ("HEAD", method) == 0) { + type = head_e; + } else if (strcmp ("POST", method) == 0) { + type = post_e; + } else { + VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method)); + return error_400 (pdata); + } + VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version)); + + /* analyse uri */ + char *filename = strtok (uri, "&"); + char *variables = strtok (NULL, "\n"); + char *path = (char *) calloc (strlen (conf->root) + strlen (filename) + 2, 1); + //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri); + sprintf (path, "%s/%s", conf->root, filename); + + /* check header */ + header_t header = {0}; + while (strcmp (line = find_sequence (data, len + data - saved_data, "\r\n", &data), "") != 0) { + VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line)); + char *field = strtok (line, ":"); + char *value = trim (strtok (NULL, "\r")); + VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value)); + if (*line == 0) { + break; + } + + VERBOSE (DEBUG, PRINT ("Analyse field\n")); + if (strcmp (field, "Allow") == 0) { header.allow = value; } + else if (strcmp (field, "Authorization") == 0) { header.authorization = value; } + else if (strcmp (field, "Content-Encoding") == 0) { header.content_encoding = value; } + else if (strcmp (field, "Content-Length") == 0) { header.content_length = value; } + else if (strcmp (field, "Content-Type") == 0) { header.content_type = value; } + else if (strcmp (field, "Date") == 0) { header.date = value; } + else if (strcmp (field, "Expires") == 0) { header.expires = value; } + else if (strcmp (field, "From") == 0) { header.from = value; } + else if (strcmp (field, "If-Modified-Since") == 0) { header.if_modified_since = value; } + else if (strcmp (field, "Last-Modified") == 0) { header.last_modified = value; } + else if (strcmp (field, "Location") == 0) { header.location = value; } + else if (strcmp (field, "Pragma") == 0) { header.pragma = value; } + else if (strcmp (field, "Referer") == 0) { header.referer = value; } + else if (strcmp (field, "Server") == 0) { header.server = value; } + else if (strcmp (field, "User-Agent") == 0) { header.user_agent = value; } + else if (strcmp (field, "WWW-Authenticate") == 0) { header.www_authenticate = value; } + else { VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field)); } + } + VERBOSE (DEBUG, print_header_values (&header)); + + /* body */ + char *body = data; + len -= saved_data - data; + if (len != (header.content_length ? atoi (header.content_length) : 0)) { + VERBOSE (WARNING, PRINT ("Incoherent size (%d <> %s)\n", len, header.content_length)); + } + + /* response */ + char *buffer = NULL; + FILE *fid = NULL; + switch (type) { + case get_e: + case post_e: /* fall through */ + VERBOSE (DEBUG, PRINT ("Read file %s\n", path)); + len = readfile (&buffer, path); + if (len < 0) { + sprintf (location, "http://%s/", conf->servername); + len = error_404 (pdata, location); + } else { + sprintf (location, "http://%s%s", conf->servername, path); + len = generic_response (pdata, location, buffer, len); + free (buffer); + } + break; + case head_e: + VERBOSE (DEBUG, PRINT ("Test file %s\n", path)); + fid = fopen (path, "rb"); + if (fid == NULL) { + sprintf (location, "http://%s/", conf->servername); + len = error_404 (pdata, location); + } else { + fclose (fid); + sprintf (location, "http://%s%s", conf->servername, path); + len = generic_response (pdata, location, NULL, 0); + } + break; + case not_supported_e: + break; + } + + /* cleaning */ + VERBOSE (DEBUG, PRINT ("Cleaning\n")); + if (path) { + VERBOSE (DEBUG, PRINT ("Cleaning path\n")); + free (path); + } + return len; }