about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2021-06-07 00:36:06 +0200
committersternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2021-06-07 00:36:06 +0200
commit0ba9f89610a55f2cec6617f3b8864a7b94df886e (patch)
tree493b41b742e7856bae92142a9bbdd9df0bfe9be5
parentbee1aa3d3c48cf9600cf0502fb78ac8ca3c9972c (diff)
refactor: return enum-ed error codes instead of HTTP status
-rw-r--r--Makefile2
-rw-r--r--entry.c45
-rw-r--r--error.c32
-rw-r--r--error.h26
-rw-r--r--index.c11
-rw-r--r--main.c25
6 files changed, 101 insertions, 40 deletions
diff --git a/Makefile b/Makefile
index ea40383..fd31f4a 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ include config.mk
 
 ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
 
-sternenblog.cgi: xml.o entry.o index.o stringutil.o cgiutil.o timeutil.o $(TEMPLATE).o main.o
+sternenblog.cgi: xml.o entry.o index.o stringutil.o cgiutil.o timeutil.o error.o $(TEMPLATE).o main.o
 	$(CC) $(CFLAGS) -o $@ $^
 
 main.o: main.c timeutil.h config.h
diff --git a/entry.c b/entry.c
index aac705d..112b2e9 100644
--- a/entry.c
+++ b/entry.c
@@ -10,15 +10,13 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "error.h"
 #include "config.h" // TODO: make independent?
 #include "cgiutil.h"
 #include "entry.h"
 
 int make_entry(const char *blog_dir, char *script_name, char *path_info, struct entry *entry) {
     // TODO: allow subdirectories?
-    // TODO: no status code return?
-
-    // TODO: url encoding of links
 
     // intialize pointers
     entry->time = 0;
@@ -33,7 +31,7 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     // validate path_info
     if(path_info == NULL) {
         fprintf(stderr, "Missing PATH_INFO\n");
-        return 500;
+        return STERNENBLOG_ERROR_CGI;
     }
 
     size_t path_info_len = strlen(path_info);
@@ -42,7 +40,7 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     // as per RFC3875 expect it to start with a slash
     if(path_info_len == 0 || path_info[0] != '/') {
         fprintf(stderr, "Malformed PATH_INFO: \"%s\"\n", path_info);
-        return 400;
+        return STERNENBLOG_ERROR_REQUEST;
     }
 
     // check if the path_info segments are alright
@@ -54,11 +52,11 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
                 case '/':
                     // TODO: necessary?
                     fprintf(stderr, "Double slash in PATH_INFO: \"%s\"\n", path_info);
-                    return 400;
+                    return STERNENBLOG_ERROR_REQUEST;
                     break;
                 case '.':
                     fprintf(stderr, "Dot file or dir in PATH_INFO: \"%s\"\n", path_info);
-                    return 403;
+                    return STERNENBLOG_ERROR_FORBIDDEN;
                     break;
                 default:
                     last_was_slash = 0;
@@ -71,7 +69,7 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     // set title (PATH_INFO without the slash)
     if(path_info_len < 2) {
         // shouldn't be called with just "/"
-        return 500;
+        return STERNENBLOG_ERROR_UNEXPECTED;
     }
 
     // title length is exactly path_info_len (-1 for slash, +1 for null byte)
@@ -97,7 +95,7 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     memset(&file_info, 0, sizeof(struct stat));
 
     if(stat(entry->path, &file_info) == -1) {
-        return http_errno(errno);
+        return error_from_errno();
     }
 
     int regular_file = (file_info.st_mode & S_IFMT) == S_IFREG;
@@ -112,9 +110,9 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     }
 
     if(!access) {
-        return http_errno(EACCES);
+        return STERNENBLOG_ERROR_FORBIDDEN;
     } else if(!regular_file) {
-        return http_errno(ENOENT);
+        return STERNENBLOG_ERROR_NOT_FOUND;
     }
 
     // use POSIX compatible version, since we don't need nanoseconds
@@ -123,7 +121,7 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     // build the link using SCRIPT_NAME
     if(script_name == NULL) {
         fprintf(stderr, "Missing SCRIPT_NAME\n");
-        return 500;
+        return STERNENBLOG_ERROR_CGI;
     }
 
     // don't check SCRIPT_NAME validity, since we
@@ -143,51 +141,54 @@ int make_entry(const char *blog_dir, char *script_name, char *path_info, struct
     entry->link[link_size - 1] = '\0';
 
     if(urlencode_realloc(&entry->link, link_size) <= 0) {
-        return 500;
+        return STERNENBLOG_ERROR_SYSTEM;
     }
 
-    return 200;
+    return STERNENBLOG_OK;
 }
 
 int entry_get_text(struct entry *entry) {
-    // TODO set errno correctly in all cases
     if(entry->text != NULL) {
         // nothing to do
-        return 0;
+        return STERNENBLOG_OK;
     }
 
     int fd = open(entry->path, O_RDONLY);
 
     if(fd == -1) {
-        return -1;
+        return error_from_errno();
     }
 
     struct stat file_info;
 
     if(fstat(fd, &file_info) == -1) {
-        return -1;
+        return error_from_errno();
     }
 
     if(file_info.st_size == 0) {
         close(fd);
-        return 0;
+        return STERNENBLOG_OK;
     }
 
     entry->text = mmap(NULL, file_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 
     if(entry->text == MAP_FAILED) {
+        int old_errno = errno;
+
         entry->text = NULL;
         close(fd);
-        return -1;
+
+        errno = old_errno;
+        return STERNENBLOG_ERROR_SYSTEM;
     }
 
     entry->text_size = file_info.st_size;
 
     if(close(fd) == -1) {
-        return -1;
+        return STERNENBLOG_ERROR_SYSTEM;
     }
 
-    return 0;
+    return STERNENBLOG_OK;
 }
 
 void entry_unget_text(struct entry *entry) {
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..1f51455
--- /dev/null
+++ b/error.c
@@ -0,0 +1,32 @@
+#include <errno.h>
+#include "error.h"
+
+int error_http_status(enum sternenblog_error err) {
+    switch(err) {
+        case STERNENBLOG_OK:
+            return 200;
+        case STERNENBLOG_ERROR_NOT_FOUND:
+            return 404;
+        case STERNENBLOG_ERROR_FORBIDDEN:
+            return 403;
+        case STERNENBLOG_ERROR_REQUEST:
+            return 400;
+        case STERNENBLOG_ERROR_SYSTEM:
+        case STERNENBLOG_ERROR_CGI:
+        case STERNENBLOG_ERROR_UNEXPECTED:
+        default:
+            return 500;
+    }
+}
+
+int error_from_errno(void) {
+    switch(errno) {
+        case EPERM:
+        case EACCES:
+            return STERNENBLOG_ERROR_FORBIDDEN;
+        case ENOENT:
+            return STERNENBLOG_ERROR_NOT_FOUND;
+        default:
+            return STERNENBLOG_ERROR_SYSTEM;
+    }
+}
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..2128eea
--- /dev/null
+++ b/error.h
@@ -0,0 +1,26 @@
+#ifndef STERNENBLOG_ERROR_H
+#define STERNENBLOG_ERROR_H
+
+enum sternenblog_error {
+  //! No error
+  STERNENBLOG_OK = 0,
+  //! libc call returned error, check `errno` for details
+  STERNENBLOG_ERROR_SYSTEM = -1,
+  //! requested item doesn't exist
+  STERNENBLOG_ERROR_NOT_FOUND = -2,
+  //! access to the requested item isn't allowed (either by system or us)
+  STERNENBLOG_ERROR_FORBIDDEN = -3,
+  //! CGI environment is not correctly set up
+  STERNENBLOG_ERROR_CGI = -4,
+  //! User send a request that doesn't make sense to us
+  STERNENBLOG_ERROR_REQUEST = -5,
+  //! The unexpected happened like an assumption not holding.
+  //! If this error is generated it is likely a bug
+  STERNENBLOG_ERROR_UNEXPECTED = -6,
+};
+
+int error_http_status(enum sternenblog_error err);
+
+int error_from_errno(void);
+
+#endif
diff --git a/index.c b/index.c
index 9cf673e..144f80a 100644
--- a/index.c
+++ b/index.c
@@ -6,6 +6,7 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "error.h"
 #include "index.h"
 
 /*!
@@ -36,11 +37,11 @@ int entries_timesort_r(struct entry *a, struct entry *b) {
 
 int make_index(const char *blog_dir, char *script_name, bool get_text, struct entry *entries[]) {
     if(*entries != NULL) {
-        return -1;
+        return STERNENBLOG_ERROR_UNEXPECTED;
     }
 
     if(script_name == NULL) {
-        return -1;
+        return STERNENBLOG_ERROR_CGI;
     }
 
     size_t index_count = 0;
@@ -49,7 +50,7 @@ int make_index(const char *blog_dir, char *script_name, bool get_text, struct en
     dir = opendir(blog_dir);
 
     if(dir == NULL) {
-        return -1;
+        return error_from_errno();
     }
 
     struct dirent *ent;
@@ -58,7 +59,7 @@ int make_index(const char *blog_dir, char *script_name, bool get_text, struct en
     *entries = malloc(sizeof(struct entry) * size);
 
     if(*entries == NULL) {
-        return -1;
+        return STERNENBLOG_ERROR_SYSTEM;
     }
 
     // losely based on musl's scandir(3)
@@ -79,7 +80,7 @@ int make_index(const char *blog_dir, char *script_name, bool get_text, struct en
 
             int result = make_entry(blog_dir, script_name, path_info, &tmp_entry);
 
-            if(result == 200) {
+            if(result == STERNENBLOG_OK) {
                 // increase array size if necessary
                 if(index_count >= size) {
                     size += BASE_INDEX_SIZE;
diff --git a/main.c b/main.c
index d7a9987..0aec826 100644
--- a/main.c
+++ b/main.c
@@ -86,6 +86,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "error.h"
 #include "config.h"
 #include "cgiutil.h"
 #include "entry.h"
@@ -127,7 +128,7 @@ int main(void) {
 
     struct entry *entries = NULL;
     int count = 0;
-    int status = 500;
+    int status = STERNENBLOG_ERROR_UNEXPECTED;
 
     // Routing: determine page_type
     // already allocate data for single entries
@@ -147,24 +148,24 @@ int main(void) {
 
     // populate data necessary for responses or switch to error page type
     if(page_type == PAGE_TYPE_INDEX || page_type == PAGE_TYPE_FEED) {
-        count = make_index(BLOG_DIR, script_name, 0, &entries);
+        status = make_index(BLOG_DIR, script_name, 0, &entries);
 
-        if(count < 0) {
-            page_type = PAGE_TYPE_ERROR;
-            status = 500;
+        if(status > 0) {
+            count = status;
+            status = STERNENBLOG_OK;
         } else {
-            status = 200;
+            page_type = PAGE_TYPE_ERROR;
         }
     } else {
         // single entry is just a special index
         entries = malloc(sizeof(struct entry));
         if(entries == NULL) {
-            status = 500;
+            status = STERNENBLOG_ERROR_SYSTEM;
         } else {
             status = make_entry(BLOG_DIR, script_name, path_info, entries);
         }
 
-        if(status == 200 && entry_get_text(entries) != -1) {
+        if(status == STERNENBLOG_OK && (status = entry_get_text(entries)) == STERNENBLOG_OK) {
             count = 1;
         } else {
             page_type = PAGE_TYPE_ERROR;
@@ -172,13 +173,13 @@ int main(void) {
     }
 
     // confirm status and page_type match
-    assert(status == 200 || page_type == PAGE_TYPE_ERROR);
-    assert(page_type != PAGE_TYPE_ERROR || status != 200);
+    assert(status == STERNENBLOG_OK || page_type == PAGE_TYPE_ERROR);
+    assert(page_type != PAGE_TYPE_ERROR || status != STERNENBLOG_OK);
 
     // initial contents of data, changed in loop for PAGE_TYPE_INDEX
     struct template_data data;
     data.page_type = page_type;
-    data.status = status;
+    data.status = error_http_status(status);
     data.script_name = script_name;
     if(path_info == NULL) {
         data.path_info = "";
@@ -201,7 +202,7 @@ int main(void) {
     // render response
     switch(page_type) {
         case PAGE_TYPE_ERROR:
-            send_standard_headers(status, "text/html");
+            send_standard_headers(error_http_status(status), "text/html");
 
             template_header(data);
             template_main(data);