about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-20 17:32:20 +0200
committersternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-20 17:32:20 +0200
commitcada2ed89e5a0b68e2cfd002bf707107fb6d7111 (patch)
tree6872f2fa3907dfd0e33277f99ba1a49a88e10a19
parentf5f1eb61204bea7ea21af9b9964b63ffb00f2184 (diff)
feat(rss): minor improvements and refactor time formatting
* add timeutil.{c,h}: utility for formatting timestamps
* wrap feed description in CDATA
* use xml_escaped where applicable
* use time of latest entry for lastBuildDate
-rw-r--r--Makefile6
-rw-r--r--main.c50
-rw-r--r--timeutil.c30
-rw-r--r--timeutil.h14
4 files changed, 71 insertions, 29 deletions
diff --git a/Makefile b/Makefile
index f39b88f..27826c4 100644
--- a/Makefile
+++ b/Makefile
@@ -2,13 +2,13 @@ include config.mk
 
 ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
 
-sternenblog.cgi: xml.o entry.o index.o cgiutil.o $(TEMPLATE).o main.o
+sternenblog.cgi: xml.o entry.o index.o cgiutil.o timeutil.o $(TEMPLATE).o main.o
 	$(CC) $(CFLAGS) -o $@ $^
 
-main.o: main.c core.h config.h
+main.o: main.c core.h timeutil.h config.h
 	$(CC) $(CFLAGS) -c -o main.o $<
 
-$(TEMPLATE).o: $(TEMPLATE).c core.h config.h xml.h cgiutil.h
+$(TEMPLATE).o: $(TEMPLATE).c core.h config.h xml.h cgiutil.h timeutil.h
 	$(CC) $(CFLAGS) -I$(ROOT_DIR) -c -o $@ $<
 
 # only invoked if config.h does not exist
diff --git a/main.c b/main.c
index b1d74c9..fb73915 100644
--- a/main.c
+++ b/main.c
@@ -88,6 +88,7 @@
 #include "cgiutil.h"
 #include "entry.h"
 #include "index.h"
+#include "timeutil.h"
 #include "template.h"
 #include "xml.h"
 
@@ -255,15 +256,6 @@ void blog_rss(char script_name[]) {
     send_header("Content-type", "application/rss+xml");
     terminate_headers();
 
-    // current time
-    time_t timestamp;
-    struct tm *timeinfo;
-    const char *rss_time_format = "%a, %d %b %G %T %z";
-    char strtime_now[64];
-
-    time(&timestamp);
-    timeinfo = localtime(&timestamp);
-
     struct xml_context ctx;
     new_xml_context(&ctx);
 
@@ -275,27 +267,34 @@ void blog_rss(char script_name[]) {
     xml_open_tag(&ctx, "channel");
 
     xml_open_tag(&ctx, "title");
-    xml_raw(&ctx, BLOG_TITLE);
+    xml_escaped(&ctx, BLOG_TITLE);
     xml_close_tag(&ctx, "title");
 
     xml_open_tag(&ctx, "description");
+    xml_open_cdata(&ctx);
     xml_raw(&ctx, BLOG_DESCRIPTION);
+    xml_close_cdata(&ctx);
     xml_close_tag(&ctx, "description");
 
     xml_open_tag(&ctx, "link");
-    xml_raw(&ctx, BLOG_SERVER_URL);
+    xml_escaped(&ctx, BLOG_SERVER_URL);
     xml_close_tag(&ctx, "link");
 
-    if(strftime(strtime_now, sizeof strtime_now, rss_time_format, timeinfo) > 0) {
-        xml_open_tag(&ctx, "lastBuildDate");
-        xml_raw(&ctx, strtime_now);
-        xml_close_tag(&ctx, "lastBuildDate");
+    if(count > 0) {
+        time_t update_time = entries[0].time;
+        char strtime_update[MAX_TIMESTR_SIZE];
+
+        if(flocaltime(strtime_update, RSS_TIME_FORMAT, MAX_TIMESTR_SIZE, &update_time) > 0) {
+            xml_open_tag(&ctx, "lastBuildDate");
+            xml_escaped(&ctx, strtime_update);
+            xml_close_tag(&ctx, "lastBuildDate");
+        }
     }
 
-    char ttl_string[64];
+    char ttl_string[32];
     if(snprintf(ttl_string, sizeof ttl_string, "%d", BLOG_RSS_TTL) >= 0) {
         xml_open_tag(&ctx, "ttl");
-        xml_raw(&ctx, ttl_string);
+        xml_escaped(&ctx, ttl_string);
         xml_close_tag(&ctx, "ttl");
     }
 
@@ -310,17 +309,17 @@ void blog_rss(char script_name[]) {
     for(int i = 0; i < count; i++) {
         xml_open_tag(&ctx, "item");
         xml_open_tag(&ctx, "title");
-        xml_raw(&ctx, entries[i].title);
+        xml_escaped(&ctx, entries[i].title);
         xml_close_tag(&ctx, "title");
 
         xml_open_tag(&ctx, "link");
-        xml_raw(&ctx, BLOG_SERVER_URL);
-        xml_raw(&ctx, entries[i].link);
+        xml_escaped(&ctx, BLOG_SERVER_URL);
+        xml_escaped(&ctx, entries[i].link);
         xml_close_tag(&ctx, "link");
 
         xml_open_tag(&ctx, "guid");
-        xml_raw(&ctx, BLOG_SERVER_URL);
-        xml_raw(&ctx, entries[i].link);
+        xml_escaped(&ctx, BLOG_SERVER_URL);
+        xml_escaped(&ctx, entries[i].link);
         xml_close_tag(&ctx, "guid");
 
         if(entries[i].text_size > 0) {
@@ -331,12 +330,11 @@ void blog_rss(char script_name[]) {
             xml_close_tag(&ctx, "description");
         }
 
-        struct tm *timeinfo_entry = localtime(&entries[i].time);
-        char strtime_entry[64];
+        char strtime_entry[MAX_TIMESTR_SIZE];
 
-        if(strftime(strtime_entry, sizeof strtime_entry, rss_time_format, timeinfo_entry) > 0) {
+        if(flocaltime(strtime_entry, RSS_TIME_FORMAT, MAX_TIMESTR_SIZE, &entries[i].time) > 0) {
             xml_open_tag(&ctx, "pubDate");
-            xml_raw(&ctx, strtime_entry);
+            xml_escaped(&ctx, strtime_entry);
             xml_close_tag(&ctx, "pubDate");
         }
 
diff --git a/timeutil.c b/timeutil.c
new file mode 100644
index 0000000..3d1cec3
--- /dev/null
+++ b/timeutil.c
@@ -0,0 +1,30 @@
+#define _POSIX_C_SOURCE 1
+#include <time.h>
+#include "timeutil.h"
+
+extern long timezone;
+
+char *format_string(enum time_format t, long tz) {
+    switch(t) {
+        case RSS_TIME_FORMAT:
+            return "%a, %d %b %Y %T %z";
+        case HTML_TIME_FORMAT_READABLE:
+            return tz == 0 ? "%Y-%m-%d %TZ" : "%Y-%m-%d %T%z";
+        case ATOM_TIME_FORMAT:
+        default:
+            return tz == 0 ? "%Y-%m-%dT%TZ" : "%Y-%m-%dT%T%z";
+    }
+}
+
+size_t flocaltime(char *b, enum time_format type, size_t size, const time_t *time) {
+    tzset();
+    struct tm *local = localtime(time);
+    char *format = format_string(type, timezone);
+
+    size_t res = strftime(b, size, format, local);
+
+    // prevent any buffer overflows
+    b[size - 1] = '\0';
+
+    return res;
+}
diff --git a/timeutil.h b/timeutil.h
new file mode 100644
index 0000000..4659513
--- /dev/null
+++ b/timeutil.h
@@ -0,0 +1,14 @@
+enum time_format {
+    RSS_TIME_FORMAT,
+    ATOM_TIME_FORMAT,
+    HTML_TIME_FORMAT_READABLE
+};
+
+#define MAX_TIMESTR_SIZE 32
+// max HTML/Atom: 24 + NUL byte
+// max RSS:       31 + NUL byte
+
+char *format_string(enum time_format t, long tz);
+
+size_t flocaltime(char *b, enum time_format type, size_t size, const time_t *time);
+