about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-25 09:01:28 +0200
committersternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-25 09:01:28 +0200
commitfecaa3cbc5b36461eacd80b8b040334b02210dd7 (patch)
treed3da86e14cf015da7bdfbc069c60bb6d65d4a265
parent5db243085b33db15901b4e43ac298e59e15c247b (diff)
refactor(main): rework routing to minimize code duplication
-rw-r--r--core.h14
-rw-r--r--main.c198
2 files changed, 103 insertions, 109 deletions
diff --git a/core.h b/core.h
index cbfd0ce..d380d24 100644
--- a/core.h
+++ b/core.h
@@ -32,3 +32,17 @@ struct entry {
     char *text;        //!< contents of the entry (mmap-ed file) or `NULL`
 };
 
+/*!
+ * @brief Type of a HTML response
+ *
+ * This enum describes the three types of HTML responses sternenblog can
+ * generate: a single entry, an index and an error page.
+ *
+ * Used internally for routing and passed to the template for context.
+ */
+enum page_type {
+  PAGE_TYPE_ENTRY,
+  PAGE_TYPE_INDEX,
+  PAGE_TYPE_ERROR
+};
+
diff --git a/main.c b/main.c
index b9b6914..dd00b25 100644
--- a/main.c
+++ b/main.c
@@ -95,31 +95,28 @@
 #include "template.h"
 #include "xml.h"
 
-void send_standard_headers(int status, char content_type[]);
-
 /*!
- * @brief Outputs the CGI response for the index page of the blog
+ * @brief Routing enum to differentiate feeds
  *
- * This function is called if `PATH_INFO` is `/` or empty.
+ * Used in routing to differentiate between
  *
- * @see template_index_entry
- * @see make_index
+ * * Feeds (RSS vs. Atom)
+ * * Feeds and non-feeds (feeds are a special type of `PAGE_TYPE_INDEX`)
  */
-void blog_index(char script_name[]);
+enum feed_type {
+    FEED_TYPE_NONE,
+    FEED_TYPE_RSS,
+    FEED_TYPE_ATOM
+};
 
 /*!
- * @brief Outputs the CGI response for a single entry
+ * @brief Send terminated default header compound
  *
- * Called if no other special routes are found (index, RSS feed).
- * If `make_entry()` succeeds, the page for the entry is displayed
- * using `template_single_entry()`. If an error occurs,
- * `template_error()` is used.
- *
- * @see make_entry
- * @see template_single_entry
- * @see template_error
+ * Sends `Status` and `Content-type` headers for the given
+ * values and a `Cache-Control` header if applicable before
+ * calling `terminate_headers()`.
  */
-void blog_entry(char script_name[], char path_info[]);
+void send_standard_headers(int status, char content_type[]);
 
 /*!
  * @brief Outputs the CGI response for the blog's RSS feed
@@ -129,7 +126,7 @@ void blog_entry(char script_name[], char path_info[]);
  * @see make_index
  * @see blog_atom
  */
-void blog_rss(char script_name[]);
+void blog_rss(char script_name[], struct entry *entries, int count);
 
 /*!
  * @brief Outputs the CGI response for the blog's Atom feed
@@ -139,7 +136,7 @@ void blog_rss(char script_name[]);
  * @see make_index
  * @see blog_rss
  */
-void blog_atom(char script_name[]);
+void blog_atom(char script_name[], struct entry *entries, int count);
 
 /*!
  * @brief Implements routing of requests
@@ -152,116 +149,115 @@ int main(void) {
     char *path_info = getenv("PATH_INFO");
     char *script_name = getenv("SCRIPT_NAME");
 
-    if(script_name == NULL) {
-        fputs("Missing SCRIPT_NAME", stderr);
-
-        send_standard_headers(500, "text/html");
+    enum page_type page_type;
+    enum feed_type is_feed = FEED_TYPE_NONE;
 
-        template_header();
-        template_error(500);
-        template_footer();
+    struct entry *entries = NULL;
+    int count = 0;
+    int status = 500;
 
-        return EXIT_SUCCESS;
-    }
+    // Routing: determine page_type and feed_type
+    // already allocate data for single entries
+    if(script_name == NULL) {
+        fputs("Missing CGI environment variable SCRIPT_NAME\n", stderr);
+        page_type = PAGE_TYPE_ERROR;
+    } else if(path_info == NULL || path_info[0] == '\0' || strcmp(path_info, "/") == 0) {
+        // make sure clean URLs are generated
+        setenv("PATH_INFO", "", 1);
 
-    if(path_info == NULL || path_info[0] == '\0' || strcmp(path_info, "/") == 0) {
-        blog_index(script_name);
+        page_type = PAGE_TYPE_INDEX;
     } else if(strcmp(path_info, "/rss.xml") == 0) {
-        blog_rss(script_name);
+        is_feed = FEED_TYPE_RSS;
+        page_type = PAGE_TYPE_INDEX;
     } else if(strcmp(path_info, "/atom.xml") == 0) {
-        blog_atom(script_name);
+        page_type = PAGE_TYPE_INDEX;
+        is_feed = FEED_TYPE_ATOM;
     } else {
-        blog_entry(script_name, path_info);
-    }
-
-    return EXIT_SUCCESS;
-}
-
-void send_standard_headers(int status, char content_type[]) {
-    send_header("Status", http_status_line(status));
-    send_header("Content-type", content_type);
-
-#ifdef BLOG_CACHE_MAX_AGE
-    // TODO correct sized buffer, no snprintf
-    char max_age[256];
-    int result = snprintf(max_age, sizeof max_age, "max-age=%d", BLOG_CACHE_MAX_AGE);
-
-    // make sure there won't be a buffer overrun
-    max_age[sizeof max_age - 1] = '\0';
+        // single entry is just a special index
+        entries = malloc(sizeof(struct entry));
+        if(entries == NULL) {
+            status = 500;
+        } else {
+            status = make_entry(BLOG_DIR, script_name, path_info, entries);
+        }
 
-    if(result > 0) {
-        send_header("Cache-Control", max_age);
+        if(status == 200 && entry_get_text(entries) != -1) {
+            page_type = PAGE_TYPE_ENTRY;
+            count = 1;
+        } else {
+            page_type = PAGE_TYPE_ERROR;
+        }
     }
-#endif
-
-    terminate_headers();
-}
 
-void blog_index(char script_name[]) {
-    struct entry *entries = NULL;
+    // construct index for feeds and index page
+    if(page_type == PAGE_TYPE_INDEX) {
+        count = make_index(BLOG_DIR, script_name, 0, &entries);
 
-    int count = make_index(BLOG_DIR, script_name, 0, &entries);
+        if(count < 0) {
+            page_type = PAGE_TYPE_ERROR;
+            status = 500;
+        } else {
+            page_type = PAGE_TYPE_INDEX;
+            status = 200;
+        }
+    }
 
-    if(count < 0) {
-        send_standard_headers(500, "text/html");
+    // render response
+    if(page_type == PAGE_TYPE_ERROR) {
+        send_standard_headers(status, "text/html");
 
         template_header();
-        template_error(500);
+        template_error(status);
         template_footer();
-    } else {
+    } else if(is_feed == FEED_TYPE_NONE) {
+        // either PAGE_TYPE_INDEX or PAGE_TYPE_ENTRY
         send_standard_headers(200, "text/html");
 
         template_header();
 
         for(int i = 0; i < count; i++) {
-            if(entry_get_text(&entries[i]) != -1) {
+            if(entries[i].text != NULL || entry_get_text(&entries[i]) != -1) {
                 template_index_entry(entries[i]);
                 entry_unget_text(&entries[i]);
             }
         }
 
         template_footer();
+    } else if(is_feed == FEED_TYPE_RSS) {
+        blog_rss(script_name, entries, count);
+    } else if(is_feed == FEED_TYPE_ATOM) {
+        blog_atom(script_name, entries, count);
+    }
 
+    // clean up
+    if(entries != NULL) {
         free_index(&entries, count);
     }
+
+    return EXIT_SUCCESS;
 }
 
-void blog_entry(char script_name[], char path_info[]) {
-    struct entry entry;
-    int status = make_entry(BLOG_DIR, script_name, path_info, &entry);
+void send_standard_headers(int status, char content_type[]) {
+    send_header("Status", http_status_line(status));
+    send_header("Content-type", content_type);
 
-    if(status == 200 && entry_get_text(&entry) == -1) {
-        status = 500;
-    }
+#ifdef BLOG_CACHE_MAX_AGE
+    // TODO correct sized buffer, no snprintf
+    char max_age[256];
+    int result = snprintf(max_age, sizeof max_age, "max-age=%d", BLOG_CACHE_MAX_AGE);
 
-    send_standard_headers(status, "text/html");
+    // make sure there won't be a buffer overrun
+    max_age[sizeof max_age - 1] = '\0';
 
-    if(status != 200) {
-        template_header();
-        template_error(status);
-        template_footer();
-    } else {
-        template_header();
-        template_single_entry(entry);
-        template_footer();
+    if(result > 0) {
+        send_header("Cache-Control", max_age);
     }
+#endif
 
-    free_entry(&entry);
+    terminate_headers();
 }
 
-void blog_rss(char script_name[]) {
-    // index
-    struct entry *entries = NULL;
-
-    int count = make_index(BLOG_DIR, script_name, 0, &entries);
-
-    if(count < 0) {
-        send_standard_headers(500, "text/plain");
-
-        puts("Internal Server Error");
-        return;
-    }
-
+void blog_rss(char script_name[], struct entry *entries, int count) {
     send_standard_headers(200, "application/rss+xml");
 
     struct xml_context ctx;
@@ -357,25 +353,10 @@ void blog_rss(char script_name[]) {
 
     free(external_url);
 
-    free_index(&entries, count);
-
     del_xml_context(&ctx);
 }
 
-void blog_atom(char script_name[]) {
-    struct entry *entries = NULL;
-
-    int count = make_index(BLOG_DIR, script_name, 0, &entries);
-
-    if(count < 0) {
-        send_standard_headers(500, "text/plain");
-
-        puts("Internal Server Error");
-
-        free_index(&entries, count);
-        return;
-    }
-
+void blog_atom(char script_name[], struct entry *entries, int count) {
     struct xml_context ctx;
     new_xml_context(&ctx);
 
@@ -481,6 +462,5 @@ void blog_atom(char script_name[]) {
 
     free(external_url);
 
-    free_index(&entries, count);
     del_xml_context(&ctx);
 }