major fixes and better tests
[webserver.git] / http.c
diff --git a/http.c b/http.c
index dce3cc86d8a81686abd9d483b60d921a2a6ec63e..fbd18ef87e67ba23e0ceabc2728e4d1c6d298e34 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,4 +1,5 @@
 #include <malloc.h>
+#include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
@@ -59,6 +60,51 @@ typedef struct {
     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)
@@ -107,9 +153,16 @@ char *find_sequence (char *data, int len, char *seq, char **pdata)
 
 int add_line (char **buffer, char *str)
 {
-    int len = strlen (*buffer) + strlen (str) + 1;
-    *buffer = realloc (*buffer, len);
+    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;
 }
 
@@ -118,7 +171,7 @@ int add_status_line (char **buffer, code_t code)
     char tmp[BUFFER_SIZE] = {0};
 
     /* Status */
-    sprintf (tmp, "%s %s\r\n", HTTP_VERSION, codes[code]);
+    sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
     add_line (buffer, tmp);
 
     return strlen (*buffer);
@@ -130,7 +183,8 @@ int add_general_header (char **buffer)
 
     /* Date */
     time_t ts = time (NULL);
-    sprintf (tmp, "Date: %s\r\n", ctime (&ts));
+    sprintf (tmp, "Date: %s", ctime (&ts));
+    tmp[strlen (tmp) - 1] = 0; // remove last \n
     add_line (buffer, tmp);
 
     /* Pragma */
@@ -143,11 +197,11 @@ int add_response_header (char **buffer, char *uri)
     char tmp[BUFFER_SIZE] = {0};
 
     /* Location */
-    sprintf (tmp, "Location: %s\r\n", uri);
+    sprintf (tmp, "Location: %s", uri);
     add_line (buffer, tmp);
 
     /* Server */
-    sprintf (tmp, "Server: %s\r\n", SERVER_NAME);
+    sprintf (tmp, "Server: %s", SERVER_NAME);
     add_line (buffer, tmp);
 
     /* WWW-Authentificate */
@@ -168,19 +222,19 @@ int add_entity (char **buffer, char *entity, int size, char *type, char *encodin
 
         /* Content-Encoding */
         if (encoding != NULL) {
-            sprintf (tmp, "Content-Encoding: %s\r\n", encoding);
+            sprintf (tmp, "Content-Encoding: %s", encoding);
             add_line (buffer, tmp);
         }
 
         /* Content-Length */
-        sprintf (tmp, "Content-Length: %d\r\n", size);
+        sprintf (tmp, "Content-Length: %d", size);
         add_line (buffer, tmp);
 
         /* Content-Type */
-        sprintf (tmp, "Content-Type: %s\r\n", type);
+        sprintf (tmp, "Content-Type: %s", type);
         add_line (buffer, tmp);
 
-        add_line (buffer, "\r\n");
+        add_line (buffer, "");
 
         /* Entity */
         len = strlen (*buffer);
@@ -216,15 +270,21 @@ int error_404 (char **buffer, char *uri)
     return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
 }
 
-/* response html */
+/* generic response */
 
-int response_html (char **buffer, char *location, char *response)
+int generic_response (char **buffer, char *location, char *response, int size)
 {
-    add_status_line (buffer, c200);
-    add_general_header (buffer);
-    add_response_header (buffer, location);
-
-    return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
+    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 */
@@ -241,13 +301,14 @@ char *trim (char *str)
 
 /* main HTTP processing */
 
-int processing (char *data, int len, char *root, char **pdata)
+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, "\r\n", &data);
+    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) {
@@ -269,19 +330,20 @@ int processing (char *data, int len, char *root, char **pdata)
         type = post_e;
     } else {
         VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
-        free (saved_data);
         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 = calloc (strlen (root) + strlen (uri) + 2, 1);
-    //sprintf (filename, "%s%s%s", root, ((root[strlen (root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
-    sprintf (filename, "%s/%s", root, 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, "\r\n", &data), "") != 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"));
@@ -291,72 +353,72 @@ int processing (char *data, int len, char *root, char **pdata)
         }
         
         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));
-        }
+        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));
 
-    /* processing data */
-    if (data) {
-        VERBOSE (DEBUG, PRINT ("Processing data: %s\n", data));
+    /* 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:
-        len = readfile (&buffer, filename);
-        if (len == 0) {
-            len = error_404 (pdata, "http://localhost/");
+    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 {
-            len = response_html (pdata, "http://localhost/", buffer);
+            sprintf (location, "http://%s%s", conf->servername, path);
+            len = generic_response (pdata, location, buffer, len);
             free (buffer);
         }
         break;
     case head_e:
-        break;
-    case post_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 */
-    free (saved_data);
-    free (filename);
+    VERBOSE (DEBUG, PRINT ("Cleaning\n"));
+    if (path) {
+        VERBOSE (DEBUG, PRINT ("Cleaning path\n"));
+        free (path);
+    }
 
     return len;
 }