diff options
author | sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> | 2020-08-20 17:32:20 +0200 |
---|---|---|
committer | sternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org> | 2020-08-20 17:32:20 +0200 |
commit | cada2ed89e5a0b68e2cfd002bf707107fb6d7111 (patch) | |
tree | 6872f2fa3907dfd0e33277f99ba1a49a88e10a19 | |
parent | f5f1eb61204bea7ea21af9b9964b63ffb00f2184 (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-- | Makefile | 6 | ||||
-rw-r--r-- | main.c | 50 | ||||
-rw-r--r-- | timeutil.c | 30 | ||||
-rw-r--r-- | timeutil.h | 14 |
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(×tamp); - timeinfo = localtime(×tamp); - 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); + |