Download raw body.
gotwebd: rss feeds for repo tags
Hello,
I'm subscribed to several rss feeds (for e.g. release feeds on github)
and I think it would be nice if gotwebd could provide an RSS feed for
the tags.
Basically, it's just an alternate way of rendering the tags page.
To do so I had to define an helper function
`gotweb_render_absolute_url' since the RSS spec[0] as far as I can
tell wants absolute URLs in some places. In doing so I discovered
that my idea of using SCRIPT_NAME for the "prefix" doesn't really
work, with the configuration per the manpage httpd sends an empty
string as SCRIPT_NAME. DOCUMENT_URI is much better, so switch to it
too. I also needed to know whether we're over https or plain http.
(will land this change as separate commit if ok)
I've tested it locally where I'm running gotwebd under a path prefix
and on my server[1], seems to work just fine. I've tested the feeds
with rssgoemail, liferea, and a validator[2], and all are happy.
What do you think?
[0]: https://www.rssboard.org/rss-specification
[1]: https://git.omarpolo.com/
[2]: https://www.rssboard.org/rss-validator/check.cgi
diff /home/op/w/got
commit - b2b1792329f5adf1e755614e710d49388845293e
path + /home/op/w/got
blob - b4f85fc2121de39b9a1c7ff2f9fcab75f5b7da42
file + gotwebd/fcgi.c
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
@@ -261,12 +261,12 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req
*sd = '\0';
}
- if (c->script_name[0] == '\0' &&
- val_len < MAX_SCRIPT_NAME &&
- name_len == 11 &&
- strncmp(buf, "SCRIPT_NAME", 11) == 0) {
- memcpy(c->script_name, val, val_len);
- c->script_name[val_len] = '\0';
+ if (c->document_uri[0] == '\0' &&
+ val_len < MAX_DOCUMENT_URI &&
+ name_len == 12 &&
+ strncmp(buf, "DOCUMENT_URI", 12) == 0) {
+ memcpy(c->document_uri, val, val_len);
+ c->document_uri[val_len] = '\0';
}
if (c->server_name[0] == '\0' &&
@@ -277,6 +277,10 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req
c->server_name[val_len] = '\0';
}
+ if (name_len == 5 &&
+ strncmp(buf, "HTTPS", 5) == 0)
+ c->https = 1;
+
buf += name_len + val_len;
n -= name_len - val_len;
}
blob - 50fee3056b1e5f7a9bc78f255201d8b1539a2995
file + gotwebd/got_operations.c
--- gotwebd/got_operations.c
+++ gotwebd/got_operations.c
@@ -572,7 +572,7 @@ got_get_repo_tags(struct request *c, int limit)
repo_dir->name) == -1)
return got_error_from_errno("asprintf");
- if (qs->commit == NULL && qs->action == TAGS) {
+ if (qs->commit == NULL && (qs->action == TAGS || qs->action == RSS)) {
error = got_ref_open(&ref, repo, qs->headref, 0);
if (error)
goto err;
blob - 28042467bba28194c9543f1b05ca9cfc5cfd644e
file + gotwebd/gotweb.c
--- gotwebd/gotweb.c
+++ gotwebd/gotweb.c
@@ -50,6 +50,7 @@
#include "proc.h"
#include "gotwebd.h"
+#include "tmpl.h"
static const struct querystring_keys querystring_keys[] = {
{ "action", ACTION },
@@ -74,6 +75,7 @@ static const struct action_keys action_keys[] = {
{ "tag", TAG },
{ "tags", TAGS },
{ "tree", TREE },
+ { "rss", RSS },
};
static const struct got_error *gotweb_init_querystring(struct querystring **);
@@ -177,6 +179,24 @@ render:
goto done;
}
+ if (qs->action == RSS) {
+ error = gotweb_render_content_type(c,
+ "application/rss+xml;charset=utf-8");
+ if (error) {
+ log_warnx("%s: %s", __func__, error->msg);
+ goto err;
+ }
+
+ error = got_get_repo_tags(c, D_MAXSLCOMMDISP);
+ if (error) {
+ log_warnx("%s: %s", __func__, error->msg);
+ goto err;
+ }
+ if (gotweb_render_rss(c->tp) == -1)
+ goto err;
+ goto done;
+ }
+
render:
error = gotweb_render_content_type(c, "text/html");
if (error) {
@@ -1624,6 +1644,8 @@ gotweb_action_name(int action)
return "tags";
case TREE:
return "tree";
+ case RSS:
+ return "rss";
default:
return NULL;
}
@@ -1722,6 +1744,21 @@ gotweb_link(struct request *c, struct gotweb_url *url,
}
int
+gotweb_render_absolute_url(struct request *c, struct gotweb_url *url)
+{
+ struct template *tp = c->tp;
+ const char *proto = c->https ? "http" : "http";
+
+ if (fcgi_puts(tp, proto) == -1 ||
+ fcgi_puts(tp, "://") == -1 ||
+ tp_htmlescape(tp, c->server_name) == -1 ||
+ tp_htmlescape(tp, c->document_uri) == -1)
+ return -1;
+
+ return gotweb_render_url(c, url);
+}
+
+int
gotweb_link(struct request *c, struct gotweb_url *url, const char *fmt, ...)
{
va_list ap;
@@ -2004,7 +2041,8 @@ gotweb_get_time_str(char **repo_age, time_t committer_
const char *hours = "hours ago", *minutes = "minutes ago";
const char *seconds = "seconds ago", *now = "right now";
char *s;
- char datebuf[29];
+ char datebuf[64];
+ size_t r;
*repo_age = NULL;
@@ -2056,6 +2094,19 @@ gotweb_get_time_str(char **repo_age, time_t committer_
if (asprintf(repo_age, "%s UTC", datebuf) == -1)
return got_error_from_errno("asprintf");
break;
+ case TM_RFC822:
+ if (gmtime_r(&committer_time, &tm) == NULL)
+ return got_error_from_errno("gmtime_r");
+
+ r = strftime(datebuf, sizeof(datebuf),
+ "%a, %d %b %Y %H:%M:%S GMT", &tm);
+ if (r == 0)
+ return got_error(GOT_ERR_NO_SPACE);
+
+ *repo_age = strdup(datebuf);
+ if (*repo_age == NULL)
+ return got_error_from_errno("asprintf");
+ break;
}
return NULL;
}
blob - fc3b18bef37416ee4320885e46d8707a3291f6c2
file + gotwebd/gotwebd.h
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -52,7 +52,7 @@
/* GOTWEB DEFAULTS */
#define MAX_QUERYSTRING 2048
-#define MAX_SCRIPT_NAME 255
+#define MAX_DOCUMENT_URI 255
#define MAX_SERVER_NAME 255
#define GOTWEB_GIT_DIR ".git"
@@ -224,8 +224,9 @@ struct request {
char querystring[MAX_QUERYSTRING];
char http_host[GOTWEBD_MAXTEXT];
- char script_name[MAX_SCRIPT_NAME];
+ char document_uri[MAX_DOCUMENT_URI];
char server_name[MAX_SERVER_NAME];
+ int https;
uint8_t request_started;
};
@@ -412,12 +413,14 @@ enum query_actions {
TAG,
TAGS,
TREE,
+ RSS,
ACTIONS__MAX,
};
enum gotweb_ref_tm {
TM_DIFF,
TM_LONG,
+ TM_RFC822,
};
extern struct gotwebd *gotwebd_env;
@@ -441,6 +444,7 @@ int gotweb_link(struct request *, struct gotweb_url *,
const struct got_error *gotweb_escape_html(char **, const char *);
const char *gotweb_action_name(int);
int gotweb_render_url(struct request *, struct gotweb_url *);
+int gotweb_render_absolute_url(struct request *, struct gotweb_url *);
int gotweb_link(struct request *, struct gotweb_url *, const char *, ...)
__attribute__((__format__(printf, 3, 4)))
__attribute__((__nonnull__(3)));
@@ -457,6 +461,7 @@ int gotweb_render_commits(struct template *);
int gotweb_render_briefs(struct template *);
int gotweb_render_navs(struct template *);
int gotweb_render_commits(struct template *);
+int gotweb_render_rss(struct template *);
/* parse.y */
int parse_config(const char *, struct gotwebd *);
blob - 6aca4b90f64838bba5e10738d6af4e2a01509ebf
file + gotwebd/pages.tmpl
--- gotwebd/pages.tmpl
+++ gotwebd/pages.tmpl
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/queue.h>
+#include <ctype.h>
#include <event.h>
#include <stdint.h>
#include <stdlib.h>
@@ -30,6 +31,9 @@ static int
#include "gotwebd.h"
#include "tmpl.h"
+static inline int rss_tag_item(struct template *, struct repo_tag *);
+static inline int rss_author(struct template *, char *);
+
static int
gotweb_render_age(struct template *tp, time_t time, int ref_tm)
{
@@ -53,7 +57,7 @@ gotweb_render_age(struct template *tp, time_t time, in
struct server *srv = c->srv;
struct querystring *qs = c->t->qs;
struct gotweb_url u_path;
- const char *prfx = c->script_name;
+ const char *prfx = c->document_uri;
const char *css = srv->custom_css;
memset(&u_path, 0, sizeof(u_path));
@@ -179,6 +183,11 @@ gotweb_render_age(struct template *tp, time_t time, in
.index_page = -1,
.page = -1,
.path = repo_dir->name,
+ }, rss = {
+ .action = RSS,
+ .index_page = -1,
+ .page = -1,
+ .path = repo_dir->name,
};
!}
<div class="index_wrapper">
@@ -211,6 +220,8 @@ gotweb_render_age(struct template *tp, time_t time, in
<a href="{{ render gotweb_render_url(tp->tp_arg, &tags) }}">tags</a>
{{ " | " }}
<a href="{{ render gotweb_render_url(tp->tp_arg, &tree) }}">tree</a>
+ {{ " | " }}
+ <a href="{{ render gotweb_render_url(tp->tp_arg, &rss) }}">rss</a>
</div>
<div class="dotted_line"></div>
</div>
@@ -389,3 +400,98 @@ gotweb_render_age(struct template *tp, time_t time, in
{{ end }}
</div>
{{ end }}
+
+{{ define gotweb_render_rss(struct template *tp) }}
+{!
+ struct request *c = tp->tp_arg;
+ struct server *srv = c->srv;
+ struct transport *t = c->t;
+ struct repo_dir *repo_dir = t->repo_dir;
+ struct repo_tag *rt;
+ struct gotweb_url summary = {
+ .action = SUMMARY,
+ .index_page = -1,
+ .page = -1,
+ .path = repo_dir->name,
+ };
+!}
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+ <channel>
+ <title>Tags of {{ repo_dir->name }}</title>
+ <link>
+ <![CDATA[
+ {{ render gotweb_render_absolute_url(c, &summary) }}
+ ]]>
+ </link>
+ {{ if srv->show_repo_description }}
+ <description>{{ repo_dir->description }}</description>
+ {{ end }}
+ {{ tailq-foreach rt &t->repo_tags entry }}
+ {{ render rss_tag_item(tp, rt) }}
+ {{ end }}
+ </channel>
+</rss>
+{{ end }}
+
+{{ define rss_tag_item(struct template *tp, struct repo_tag *rt) }}
+{!
+ struct request *c = tp->tp_arg;
+ struct transport *t = c->t;
+ struct repo_dir *repo_dir = t->repo_dir;
+ char *tag_name = rt->tag_name;
+ struct gotweb_url tag = {
+ .action = TAG,
+ .index_page = -1,
+ .page = -1,
+ .path = repo_dir->name,
+ .commit = rt->commit_id,
+ };
+
+ if (strncmp(tag_name, "refs/tags/", 10) == 0)
+ tag_name += 10;
+!}
+<item>
+ <title>{{ repo_dir->name }} {{" "}} {{ tag_name }}</title>
+ <link>
+ <![CDATA[
+ {{ render gotweb_render_absolute_url(c, &tag) }}
+ ]]>
+ </link>
+ <description>
+ <![CDATA[<pre>{{ rt->tag_commit }}</pre>]]>
+ </description>
+ {{ render rss_author(tp, rt->tagger) }}
+ <guid isPermaLink="false">{{ rt->commit_id }}</guid>
+ <pubDate>
+ {{ render gotweb_render_age(tp, rt->tagger_time, TM_RFC822) }}
+ </pubDate>
+</item>
+{{ end }}
+
+{{ define rss_author(struct template *tp, char *author) }}
+{!
+ char *t, *mail;
+
+ /* what to do if the author name contains a paren? */
+ if (strchr(author, '(') != NULL || strchr(author, ')') != NULL)
+ return 0;
+
+ t = strchr(author, '<');
+ if (t == NULL)
+ return 0;
+ *t = '\0';
+ mail = t+1;
+
+ while (isspace((unsigned char)*--t))
+ *t = '\0';
+
+ t = strchr(mail, '>');
+ if (t == NULL)
+ return 0;
+ *t = '\0';
+!}
+<author>
+ {{ mail }} {{" "}} ({{ author }})
+</author>
+{{ end }}
gotwebd: rss feeds for repo tags