Commit Diff


commit - d19d9fcec89a7d18ca8325b70f5edde7b4e6369a
commit + 1abb18e1777172a9f4149a0f50c4cecfd024f02c
blob - 89fbdaf8d8a786bf4d6905228fbfa6fcc47cf03d
blob + dd6dbe5f5b29eee48c7bcc972741e3a471d95c6b
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
@@ -276,6 +276,10 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req
 			memcpy(c->server_name, val, val_len);
 			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
blob + 89718fdd602637e74ca5a949af1e7cfda57b723d
--- 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
blob + 6030878cfe25783394593d7d14af1bd6e577f4be
--- 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 **);
@@ -170,10 +172,28 @@ gotweb_process_request(struct request *c)
 		if (error)
 			goto done;
 		error = got_output_file_blob(c);
+		if (error) {
+			log_warnx("%s: %s", __func__, error->msg);
+			goto err;
+		}
+		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;
 	}
 
@@ -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_render_url(struct request *c, struct gotweb_url
 }
 
 int
+gotweb_render_absolute_url(struct request *c, struct gotweb_url *url)
+{
+	struct template	*tp = c->tp;
+	const char	*proto = c->https ? "https" : "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 - 38161394053fe7a7aca451c50b25e97bb1f80898
blob + c54fe3683dc1d915d6d3fa880753b93adc5252e5
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -226,6 +226,7 @@ struct request {
 	char				 http_host[GOTWEBD_MAXTEXT];
 	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 @@ const struct got_error *gotweb_init_transport(struct t
 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_repo_fragment(struct template *, str
 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 - a822e44c28412ddbf85071a448bc1fca3266812d
blob + d694fb2afcffcc2b7f3319fcff1c1f49c6545ed5
--- 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 @@
 #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)
 {
@@ -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>
@@ -388,4 +399,99 @@ gotweb_render_age(struct template *tp, time_t time, in
     {{ render gotweb_render_navs(tp) }}
   {{ 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 }}