diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | config.example.h | 16 | ||||
-rw-r--r-- | doc/man/man1/sternenblog.cgi.1 | 8 | ||||
-rw-r--r-- | main.c | 134 | ||||
-rw-r--r-- | templates/simple.c | 51 |
6 files changed, 172 insertions, 40 deletions
diff --git a/Makefile b/Makefile index 4909d6d..97529d6 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ sternenblog.cgi: xml.o entry.o index.o stringutil.o cgiutil.o timeutil.o $(TEMPL 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 timeutil.h +$(TEMPLATE).o: $(TEMPLATE).c core.h config.h xml.h cgiutil.h timeutil.h stringutil.h $(CC) $(CFLAGS) -I$(ROOT_DIR) -c -o $@ $< entry.o: config.h entry.c diff --git a/TODO b/TODO index f4e9b92..653446f 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ make RSS_TTL optional | id:32a848e82d8b39bb8495f49d491882da320a3c7d -atom feed | id:44aca4a04ff19c567e08968370cc535139807bbd add support for caching | id:472d68f1af5489020f4c5808e805822e3da77ffc rethink logging / debug output | id:5734e2717f28d80ac65d3f2123a0f46c01d4a0c1 if possible determine BLOG_SERVER_URL automatically | id:5ad1fabab42a5776ab1446d09659c7a99a104245 diff --git a/config.example.h b/config.example.h index dad9dad..850a2ca 100644 --- a/config.example.h +++ b/config.example.h @@ -54,10 +54,24 @@ #define BLOG_TITLE "sternenblog" /*! + * @brief Site's Author + * + * Name of the author of the site served. + * Used to set the feed's author in the atom feed, + * ignored for RSS (since it is not recommended to + * use for single author feeds there which is the only type + * sternenblog can handle). + * + * Optional setting, if missing replaced by the username + * sternenblog is running as. + */ +#define BLOG_AUTHOR "Jane Doe" + +/*! * @brief Site description * * Description of the site to serve. - * Will be used for the RSS feed and may be utilized by templates. + * Will be used for the feeds and may be utilized by templates. * * @see https://cyber.harvard.edu/rss/rss.html#requiredChannelElements */ diff --git a/doc/man/man1/sternenblog.cgi.1 b/doc/man/man1/sternenblog.cgi.1 index 10df672..48c1bee 100644 --- a/doc/man/man1/sternenblog.cgi.1 +++ b/doc/man/man1/sternenblog.cgi.1 @@ -18,10 +18,12 @@ The Request URL .Ql /sternenblog.cgi results in the index page being served, .Ql /sternenblog.cgi/rss.xml -returns a RSS feed with the same contents as the index page and -.Ql /sternenblog.cgi/my-entry +returns a RSS feed with the same contents as the index page, likewise +.Ql /sternenblog.cgi/atom.xml +an atom feed and finally +.Ql /sternenblog.cgi/<my-entry> serves the single entry page for -.Pa /path/to/entry/directory/my-entry . +.Pa /path/to/entry/directory/<my-entry> . .Pp Every entry is a regular file in the configured entry directory that meets certain criteria (see diff --git a/main.c b/main.c index b180d3d..2636236 100644 --- a/main.c +++ b/main.c @@ -77,11 +77,13 @@ #include <stdio.h> #include <stdlib.h> #include <dirent.h> +#include <pwd.h> #include <sys/mman.h> #include <sys/types.h> #include <string.h> #include <errno.h> #include <time.h> +#include <unistd.h> #include "core.h" #include "config.h" @@ -128,10 +130,21 @@ void blog_entry(char script_name[], char path_info[]); * This function is called if `PATH_INFO` is `/rss.xml`. * * @see make_index + * @see blog_atom */ void blog_rss(char script_name[]); /*! + * @brief Outputs the CGI response for the blog's Atom feed + * + * This function is called if `PATH_INFO` is `/atom.xml`. + * + * @see make_index + * @see blog_rss + */ +void blog_atom(char script_name[]); + +/*! * @brief Implements routing of requests * * @see blog_index @@ -162,8 +175,7 @@ int main(void) { } else if(strcmp(path_info, "/rss.xml") == 0) { blog_rss(script_name); } else if(strcmp(path_info, "/atom.xml") == 0) { - // TODO - return EXIT_FAILURE; + blog_atom(script_name); } else { blog_entry(script_name, path_info); } @@ -349,3 +361,121 @@ void blog_rss(char script_name[]) { del_xml_context(&ctx); } + +void blog_atom(char script_name[]) { + struct entry *entries = NULL; + + int count = make_index(BLOG_DIR, script_name, 1, &entries); + + if(count < 0) { + send_header("Status", http_status_line(500)); + send_header("Content-type", "text/plain"); + terminate_headers(); + + puts("Internal Server Error"); + + free_index(&entries, count); + return; + } + + struct xml_context ctx; + new_xml_context(&ctx); + + char *self_url = catn_alloc(3, BLOG_SERVER_URL, script_name, "/atom.xml"); + char *html_url = catn_alloc(2, BLOG_SERVER_URL, script_name); + + send_header("Status", http_status_line(200)); + send_header("Content-type", "application/atom+xml"); + terminate_headers(); + + xml_raw(&ctx, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"); + xml_open_tag_attrs(&ctx, "feed", 1, "xmlns", "http://www.w3.org/2005/Atom"); + + xml_open_tag(&ctx, "title"); + xml_escaped(&ctx, BLOG_TITLE); + xml_close_tag(&ctx, "title"); + + if(self_url != NULL) { + xml_open_tag(&ctx, "id"); + xml_escaped(&ctx, self_url); + xml_close_tag(&ctx, "id"); + + xml_empty_tag(&ctx, "link", 2, "rel", "self", "href", self_url); + free(self_url); + } + + if(html_url != NULL) { + xml_empty_tag(&ctx, "link", 3, "rel", "alternate", "type", "text/html", "href", html_url); + // freed after author element + } + + xml_open_tag(&ctx, "author"); + xml_open_tag(&ctx, "name"); +#ifdef BLOG_AUTHOR + xml_escaped(&ctx, BLOG_AUTHOR); +#else + struct passwd *user; + uid_t uid = geteuid(); + user = getpwuid(uid); + xml_escaped(&ctx, user->pw_name); +#endif + xml_close_tag(&ctx, "name"); + + if(html_url != NULL) { + xml_open_tag(&ctx, "uri"); + xml_escaped(&ctx, html_url); + xml_close_tag(&ctx, "uri"); + free(html_url); + } + + xml_close_tag(&ctx, "author"); + + if(count > 0) { + time_t update_time = entries[0].time; + char strtime_update[MAX_TIMESTR_SIZE]; + if(flocaltime(strtime_update, ATOM_TIME_FORMAT, MAX_TIMESTR_SIZE, &update_time) > 0) { + xml_open_tag(&ctx, "updated"); + xml_escaped(&ctx, strtime_update); + xml_close_tag(&ctx, "updated"); + } + } + + for(int i = 0; i < count; i++) { + xml_open_tag(&ctx, "entry"); + + xml_open_tag(&ctx, "id"); + xml_escaped(&ctx, BLOG_SERVER_URL); + xml_escaped(&ctx, entries[i].link); + xml_close_tag(&ctx, "id"); + + xml_open_tag(&ctx, "title"); + xml_escaped(&ctx, entries[i].title); + xml_close_tag(&ctx, "title"); + + char strtime_entry[MAX_TIMESTR_SIZE]; + if(flocaltime(strtime_entry, ATOM_TIME_FORMAT, MAX_TIMESTR_SIZE, &entries[i].time) > 0) { + xml_open_tag(&ctx, "updated"); + xml_escaped(&ctx, strtime_entry); + xml_close_tag(&ctx, "updated"); + } + + char *entry_url = catn_alloc(2, BLOG_SERVER_URL, entries[i].link); + if(entry_url != NULL) { + xml_empty_tag(&ctx, "link", 3, "rel", "alternate", "type", "text/html", "href", entry_url); + free(entry_url); + } + + xml_open_tag_attrs(&ctx, "content", 1, "type", "html"); + xml_open_cdata(&ctx); + xml_raw(&ctx, entries[i].text); + xml_close_cdata(&ctx); + xml_close_tag(&ctx, "content"); + + xml_close_tag(&ctx, "entry"); + } + + xml_close_tag(&ctx, "feed"); + + free_index(&entries, count); + del_xml_context(&ctx); +} diff --git a/templates/simple.c b/templates/simple.c index 06d69e8..eeee6db 100644 --- a/templates/simple.c +++ b/templates/simple.c @@ -7,40 +7,12 @@ #include <template.h> #include <config.h> #include <cgiutil.h> +#include <stringutil.h> #include <timeutil.h> #include <xml.h> static struct xml_context ctx; -int make_link(char *buf, size_t size, char *abs_path) { - // TODO pass to template somehow? - char *script_name = getenv("SCRIPT_NAME"); - - if(script_name == NULL || buf == NULL || abs_path == NULL) { - return -1; - } - - size_t script_name_len = strlen(script_name); - size_t abs_path_len = strlen(abs_path); - - size_t min_buf_len = script_name_len + abs_path_len + 1; - - if(min_buf_len > size) { - return -1; - } - - if(script_name_len > 0) { - memcpy(buf, script_name, script_name_len); - } - if(abs_path_len > 0) { - memcpy(buf + script_name_len, abs_path, abs_path_len); - } - - buf[script_name_len + abs_path_len] = '\0'; - - return min_buf_len; -} - void output_entry_time(struct xml_context *ctx, struct entry entry) { char strtime[MAX_TIMESTR_SIZE]; @@ -90,11 +62,26 @@ void template_footer(void) { xml_open_tag(&ctx, "footer"); - char rss_link[256]; - if(make_link(rss_link, sizeof rss_link, "/rss.xml") != -1) { + char *script_name = getenv("SCRIPT_NAME"); + char *rss_link = catn_alloc(2, script_name, "/rss.xml"); + char *atom_link = catn_alloc(2, script_name, "/atom.xml"); + + if(rss_link != NULL) { xml_open_tag_attrs(&ctx, "a", 1, "href", rss_link); - xml_raw(&ctx, "RSS Feed"); + xml_escaped(&ctx, "RSS Feed"); xml_close_tag(&ctx, "a"); + + free(rss_link); + } + + if(atom_link != NULL) { + xml_raw(&ctx, " • "); + + xml_open_tag_attrs(&ctx, "a", 1, "href", atom_link); + xml_escaped(&ctx, "Atom Feed"); + xml_close_tag(&ctx, "a"); + + free(atom_link); } xml_close_all(&ctx); |