commit a36b901d404f4d4268384a379fd040898f78b1b3
Author:     Laslo Hunhold <[email protected]>
AuthorDate: Sat Aug 29 00:42:54 2020 +0200
Commit:     Laslo Hunhold <[email protected]>
CommitDate: Sat Aug 29 00:49:16 2020 +0200

    Add http_send_body() and data_send_error() and refactor
    
    This turns the data-functions into the only functions "allowed"
    to send body-data (called with http_send_body()). The previous (hacky)
    approach of doing this in http_send_header() is not only out of place,
    it's an easy source of bugs given, for instance, the sending of body
    data is not expected with HEAD-requests.
    
    Given html_escape() is now only used in data.c, we move it there from
    util.c and make it a static method again.
    
    Signed-off-by: Laslo Hunhold <[email protected]>

diff --git a/data.c b/data.c
index 46486aa..3b6b2e5 100644
--- a/data.c
+++ b/data.c
@@ -38,10 +38,55 @@ suffix(int t)
        return "";
 }
 
+static void
+html_escape(const char *src, char *dst, size_t dst_siz)
+{
+       const struct {
+               char c;
+               char *s;
+       } escape[] = {
+               { '&',  "&amp;"  },
+               { '<',  "&lt;"   },
+               { '>',  "&gt;"   },
+               { '"',  "&quot;" },
+               { '\'', "&#x27;" },
+       };
+       size_t i, j, k, esclen;
+
+       for (i = 0, j = 0; src[i] != '\0'; i++) {
+               for (k = 0; k < LEN(escape); k++) {
+                       if (src[i] == escape[k].c) {
+                               break;
+                       }
+               }
+               if (k == LEN(escape)) {
+                       /* no escape char at src[i] */
+                       if (j == dst_siz - 1) {
+                               /* silent truncation */
+                               break;
+                       } else {
+                               dst[j++] = src[i];
+                       }
+               } else {
+                       /* escape char at src[i] */
+                       esclen = strlen(escape[k].s);
+
+                       if (j >= dst_siz - esclen) {
+                               /* silent truncation */
+                               break;
+                       } else {
+                               memcpy(&dst[j], escape[k].s, esclen);
+                               j += esclen;
+                       }
+               }
+       }
+       dst[j] = '\0';
+}
+
 enum status
 data_send_dirlisting(int fd, const struct response *res)
 {
-       enum status ret;
+       enum status ret = 0;
        struct dirent **e;
        size_t i;
        int dirlen;
@@ -52,6 +97,17 @@ data_send_dirlisting(int fd, const struct response *res)
                return S_FORBIDDEN;
        }
 
+       /* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
+       html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
+       if (dprintf(fd,
+                   "<!DOCTYPE html>\n<html>\n\t<head>"
+                   "<title>Index of %s</title></head>\n"
+                   "\t<body>\n\t\t<a href=\"..\">..</a>",
+                   esc) < 0) {
+               ret = S_REQUEST_TIMEOUT;
+               goto cleanup;
+       }
+
        /* listing */
        for (i = 0; i < (size_t)dirlen; i++) {
                /* skip hidden files, "." and ".." */
@@ -86,6 +142,21 @@ cleanup:
        return ret;
 }
 
+enum status
+data_send_error(int fd, const struct response *res)
+{
+       if (dprintf(fd,
+                   "<!DOCTYPE html>\n<html>\n\t<head>\n"
+                   "\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
+                   "\t\t<h1>%d %s</h1>\n\t</body>\n</html>\n",
+                   res->status, status_str[res->status],
+                   res->status, status_str[res->status]) < 0) {
+               return S_REQUEST_TIMEOUT;
+       }
+
+       return 0;
+}
+
 enum status
 data_send_file(int fd, const struct response *res)
 {
diff --git a/data.h b/data.h
index 12aacc8..91aedf5 100644
--- a/data.h
+++ b/data.h
@@ -5,6 +5,7 @@
 #include "http.h"
 
 enum status data_send_dirlisting(int, const struct response *);
+enum status data_send_error(int, const struct response *);
 enum status data_send_file(int, const struct response *);
 
 #endif /* DATA_H */
diff --git a/http.c b/http.c
index 6e32fba..96e673a 100644
--- a/http.c
+++ b/http.c
@@ -17,6 +17,7 @@
 #include <unistd.h>
 
 #include "config.h"
+#include "data.h"
 #include "http.h"
 #include "util.h"
 
@@ -57,10 +58,16 @@ const char *res_field_str[] = {
        [RES_CONTENT_TYPE]   = "Content-Type",
 };
 
+enum status (* const body_fct[])(int, const struct response *) = {
+       [RESTYPE_ERROR]      = data_send_error,
+       [RESTYPE_FILE]       = data_send_file,
+       [RESTYPE_DIRLISTING] = data_send_dirlisting,
+};
+
 enum status
 http_send_header(int fd, const struct response *res)
 {
-       char t[FIELD_MAX], esc[PATH_MAX];
+       char t[FIELD_MAX];
        size_t i;
 
        if (timestamp(t, sizeof(t), time(NULL))) {
@@ -88,27 +95,6 @@ http_send_header(int fd, const struct response *res)
                return S_REQUEST_TIMEOUT;
        }
 
-       /* listing header */
-       if (res->type == RESTYPE_DIRLISTING) {
-               html_escape(res->uri, esc, sizeof(esc));
-               if (dprintf(fd,
-                           "<!DOCTYPE html>\n<html>\n\t<head>"
-                           "<title>Index of %s</title></head>\n"
-                           "\t<body>\n\t\t<a href=\"..\">..</a>",
-                           esc) < 0) {
-                       return S_REQUEST_TIMEOUT;
-               }
-       } else if (res->type == RESTYPE_ERROR) {
-               if (dprintf(fd,
-                           "<!DOCTYPE html>\n<html>\n\t<head>\n"
-                           "\t\t<title>%d %s</title>\n\t</head>\n\t<body>\n"
-                           "\t\t<h1>%d %s</h1>\n\t</body>\n</html>\n",
-                           res->status, status_str[res->status],
-                           res->status, status_str[res->status]) < 0) {
-                       return S_REQUEST_TIMEOUT;
-               }
-       }
-
        return 0;
 }
 
@@ -854,3 +840,16 @@ http_prepare_error_response(const struct request *req,
                }
        }
 }
+
+enum status
+http_send_body(int fd, const struct response *res,
+               const struct request *req)
+{
+       enum status s;
+
+       if (req->method == M_GET && (s = body_fct[res->type](fd, res))) {
+               return s;
+       }
+
+       return 0;
+}
diff --git a/http.h b/http.h
index d75d8d0..3d6d4d2 100644
--- a/http.h
+++ b/http.h
@@ -82,11 +82,13 @@ struct response {
        } file;
 };
 
+extern enum status (* const body_fct[])(int, const struct response *);
+
 enum conn_state {
        C_VACANT,
        C_RECV_HEADER,
        C_SEND_HEADER,
-       C_SEND_DATA,
+       C_SEND_BODY,
        NUM_CONN_STATES,
 };
 
@@ -107,5 +109,7 @@ void http_prepare_response(const struct request *, struct 
response *,
                            const struct server *);
 void http_prepare_error_response(const struct request *,
                                  struct response *, enum status);
+enum status http_send_body(int, const struct response *,
+                           const struct request *);
 
 #endif /* HTTP_H */
diff --git a/main.c b/main.c
index 2946a0b..739e2fa 100644
--- a/main.c
+++ b/main.c
@@ -45,15 +45,9 @@ serve(int infd, const struct sockaddr_storage *in_sa, const 
struct server *srv)
                http_prepare_response(&c.req, &c.res, srv);
        }
 
-       if ((s = http_send_header(c.fd, &c.res))) {
+       if ((s = http_send_header(c.fd, &c.res)) ||
+           (s = http_send_body(c.fd, &c.res, &c.req))) {
                c.res.status = s;
-       } else {
-               /* send data */
-               if (c.res.type == RESTYPE_FILE) {
-                       data_send_file(c.fd, &c.res);
-               } else if (c.res.type == RESTYPE_DIRLISTING) {
-                       data_send_dirlisting(c.fd, &c.res);
-               }
        }
 
        /* write output to log */
diff --git a/util.c b/util.c
index 2b54df1..b281613 100644
--- a/util.c
+++ b/util.c
@@ -123,51 +123,6 @@ prepend(char *str, size_t size, const char *prefix)
        return 0;
 }
 
-void
-html_escape(const char *src, char *dst, size_t dst_siz)
-{
-       const struct {
-               char c;
-               char *s;
-       } escape[] = {
-               { '&',  "&amp;"  },
-               { '<',  "&lt;"   },
-               { '>',  "&gt;"   },
-               { '"',  "&quot;" },
-               { '\'', "&#x27;" },
-       };
-       size_t i, j, k, esclen;
-
-       for (i = 0, j = 0; src[i] != '\0'; i++) {
-               for (k = 0; k < LEN(escape); k++) {
-                       if (src[i] == escape[k].c) {
-                               break;
-                       }
-               }
-               if (k == LEN(escape)) {
-                       /* no escape char at src[i] */
-                       if (j == dst_siz - 1) {
-                               /* silent truncation */
-                               break;
-                       } else {
-                               dst[j++] = src[i];
-                       }
-               } else {
-                       /* escape char at src[i] */
-                       esclen = strlen(escape[k].s);
-
-                       if (j >= dst_siz - esclen) {
-                               /* silent truncation */
-                               break;
-                       } else {
-                               memcpy(&dst[j], escape[k].s, esclen);
-                               j += esclen;
-                       }
-               }
-       }
-       dst[j] = '\0';
-}
-
 #define        INVALID  1
 #define        TOOSMALL 2
 #define        TOOLARGE 3
diff --git a/util.h b/util.h
index 6b6d17d..bc7f8ec 100644
--- a/util.h
+++ b/util.h
@@ -52,7 +52,6 @@ void eunveil(const char *, const char *);
 int timestamp(char *, size_t, time_t);
 int esnprintf(char *, size_t, const char *, ...);
 int prepend(char *, size_t, const char *);
-void html_escape(const char *, char *, size_t);
 
 void *reallocarray(void *, size_t, size_t);
 long long strtonum(const char *, long long, long long, const char **);

Reply via email to