Commit Diff


commit - 2c02675e51b8f7413665e81e1e2f3bf77ee9acb7
commit + ed619ca07e51b9c984c8404ca2b1153efdb14d1e
blob - 8744ffb84ade1da058f9e9837fdbbd91cd5130fc
blob + 1f2783a446360c7e3379bbca5be3bea0ee1e76c5
--- gotwebd/Makefile
+++ gotwebd/Makefile
@@ -1,4 +1,5 @@
 .PATH:${.CURDIR}/../lib
+.PATH:${.CURDIR}/../template
 
 SUBDIR = libexec
 
@@ -7,7 +8,7 @@ SUBDIR = libexec
 
 PROG =		gotwebd
 SRCS =		config.c sockets.c log.c gotwebd.c parse.y proc.c \
-		fcgi.c gotweb.c got_operations.c
+		fcgi.c gotweb.c got_operations.c tmpl.c pages.c
 SRCS +=		blame.c commit_graph.c delta.c diff.c \
 		diffreg.c error.c fileindex.c object.c object_cache.c \
 		object_idset.c object_parse.c opentemp.c path.c pack.c \
@@ -21,9 +22,20 @@ SRCS +=		blame.c commit_graph.c delta.c diff.c \
 		object_open_privsep.c read_gitconfig_privsep.c \
 		read_gotconfig_privsep.c pollfd.c reference_parse.c
 
+.if exists(${.CURDIR}/../template/obj/template)
+TEMPLATE = ${.CURDIR}/../template/obj/template
+.else
+TEMPLATE = ${.CURDIR}/../template/template
+.endif
+
+.SUFFIXES: .tmpl
+.tmpl.c:
+	${TEMPLATE} -o $@ $<
+
 MAN =		${PROG}.conf.5 ${PROG}.8
 
 CPPFLAGS +=	-I${.CURDIR}/../include -I${.CURDIR}/../lib -I${.CURDIR}
+CPPFLAGS +=	-I${.CURDIR}/../template
 LDADD +=	-lz -levent -lutil -lm
 YFLAGS =
 DPADD =		${LIBEVENT} ${LIBUTIL}
blob - 04e9e72ece31adeacc0aef6f6fb7cffbaace078a
blob + b4f85fc2121de39b9a1c7ff2f9fcab75f5b7da42
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
@@ -36,6 +36,7 @@
 
 #include "proc.h"
 #include "gotwebd.h"
+#include "tmpl.h"
 
 size_t	 fcgi_parse_record(uint8_t *, size_t, struct request *);
 void	 fcgi_parse_begin_request(uint8_t *, uint16_t, struct request *,
@@ -288,6 +289,21 @@ fcgi_timeout(int fd, short events, void *arg)
 }
 
 int
+fcgi_puts(struct template *tp, const char *str)
+{
+	if (str == NULL)
+		return 0;
+	return fcgi_gen_binary_response(tp->tp_arg, str, strlen(str));
+}
+
+int
+fcgi_putc(struct template *tp, int ch)
+{
+	uint8_t c = ch;
+	return fcgi_gen_binary_response(tp->tp_arg, &c, 1);
+}
+
+int
 fcgi_vprintf(struct request *c, const char *fmt, va_list ap)
 {
 	char *str;
@@ -483,6 +499,7 @@ fcgi_cleanup_request(struct request *c)
 		event_del(&c->ev);
 
 	close(c->fd);
+	template_free(c->tp);
 	gotweb_free_transport(c->t);
 	free(c);
 }
blob - fc0c94f6c50f1675561ef2a30f20c748df22416c
blob + 0ce90275bab1028c73efcf66d2d59a727f7ca7c7
--- gotwebd/gotweb.c
+++ gotwebd/gotweb.c
@@ -51,11 +51,6 @@
 #include "proc.h"
 #include "gotwebd.h"
 
-enum gotweb_ref_tm {
-	TM_DIFF,
-	TM_LONG,
-};
-
 static const struct querystring_keys querystring_keys[] = {
 	{ "action",		ACTION },
 	{ "commit",		COMMIT },
@@ -86,8 +81,6 @@ static const struct got_error *gotweb_parse_querystrin
     char *);
 static const struct got_error *gotweb_assign_querystring(struct querystring **,
     char *, char *);
-static const struct got_error *gotweb_render_header(struct request *);
-static const struct got_error *gotweb_render_footer(struct request *);
 static const struct got_error *gotweb_render_index(struct request *);
 static const struct got_error *gotweb_init_repo_dir(struct repo_dir **,
     const char *);
@@ -97,9 +90,7 @@ static const struct got_error *gotweb_get_repo_descrip
     struct server *, const char *, int);
 static const struct got_error *gotweb_get_clone_url(char **, struct server *,
     const char *, int);
-static const struct got_error *gotweb_render_navs(struct request *);
 static const struct got_error *gotweb_render_blame(struct request *);
-static const struct got_error *gotweb_render_briefs(struct request *);
 static const struct got_error *gotweb_render_commits(struct request *);
 static const struct got_error *gotweb_render_diff(struct request *);
 static const struct got_error *gotweb_render_summary(struct request *);
@@ -108,6 +99,8 @@ static const struct got_error *gotweb_render_tags(stru
 static const struct got_error *gotweb_render_tree(struct request *);
 static const struct got_error *gotweb_render_branches(struct request *);
 
+const struct got_error *gotweb_render_navs(struct request *);
+
 static void gotweb_free_querystring(struct querystring *);
 static void gotweb_free_repo_dir(struct repo_dir *);
 
@@ -195,11 +188,8 @@ render:
 	}
 	html = 1;
 
-	error = gotweb_render_header(c);
-	if (error) {
-		log_warnx("%s: %s", __func__, error->msg);
+	if (gotweb_render_header(c->tp) == -1)
 		goto err;
-	}
 
 	if (error2) {
 		error = error2;
@@ -215,11 +205,8 @@ render:
 		}
 		break;
 	case BRIEFS:
-		error = gotweb_render_briefs(c);
-		if (error) {
-			log_warnx("%s: %s", __func__, error->msg);
+		if (gotweb_render_briefs(c->tp) == -1)
 			goto err;
-		}
 		break;
 	case COMMITS:
 		error = gotweb_render_commits(c);
@@ -296,7 +283,7 @@ err:
 		return;
 done:
 	if (html && srv != NULL)
-		gotweb_render_footer(c);
+		gotweb_render_footer(c->tp);
 }
 
 struct server *
@@ -691,146 +678,8 @@ gotweb_render_content_type_file(struct request *c, con
 	    type, file);
 	return NULL;
 }
-
-static const struct got_error *
-gotweb_render_header(struct request *c)
-{
-	const struct got_error *err = NULL;
-	struct server *srv = c->srv;
-	struct querystring *qs = c->t->qs;
-	int r;
-
-	r = fcgi_printf(c, "<!doctype html>\n"
-	    "<html>\n"
-	    "<head>\n"
-	    "<title>%s</title>\n"
-	    "<meta charset='utf-8' />\n"
-	    "<meta name='viewport' content='initial-scale=.75' />\n"
-	    "<meta name='msapplication-TileColor' content='#da532c' />\n"
-	    "<meta name='theme-color' content='#ffffff'/>\n"
-	    "<link rel='apple-touch-icon' sizes='180x180'"
-	    " href='%sapple-touch-icon.png' />\n"
-	    "<link rel='icon' type='image/png' sizes='32x32'"
-	    " href='%sfavicon-32x32.png' />\n"
-	    "<link rel='icon' type='image/png' sizes='16x16'"
-	    " href='%sfavicon-16x16.png' />\n"
-	    "<link rel='manifest' href='%ssite.webmanifest'/>\n"
-	    "<link rel='mask-icon' href='%ssafari-pinned-tab.svg' />\n"
-	    "<link rel='stylesheet' type='text/css' href='%s%s' />\n"
-	    "</head>\n"
-	    "<body>\n"
-	    "<div id='gw_body'>\n"
-	    "<div id='header'>\n"
-	    "<div id='got_link'>"
-	    "<a href='%s' target='_blank'>"
-	    "<img src='%s%s' alt='logo' id='logo' />"
-	    "</a>\n"
-	    "</div>\n"		/* #got_link */
-	    "</div>\n"		/* #header */
-	    "<div id='site_path'>\n"
-	    "<div id='site_link'>\n"
-	    "<a href='?index_page=%d'>%s</a>",
-	    srv->site_name,
-	    c->script_name,
-	    c->script_name,
-	    c->script_name,
-	    c->script_name,
-	    c->script_name,
-	    c->script_name, srv->custom_css,
-	    srv->logo_url,
-	    c->script_name, srv->logo,
-	    qs->index_page, srv->site_link);
-	if (r == -1)
-		goto done;
 
-	if (qs->path != NULL) {
-		char *epath;
-
-		if (fcgi_printf(c, " / ") == -1)
-			goto done;
-
-		err = gotweb_escape_html(&epath, qs->path);
-		if (err)
-			return err;
-		r = gotweb_link(c, &(struct gotweb_url){
-			    .action = SUMMARY,
-			    .index_page = -1,
-			    .page = -1,
-			    .path = qs->path,
-		    }, "%s", epath);
-		free(epath);
-		if (r == -1)
-			goto done;
-	}
-	if (qs->action != INDEX) {
-		const char *action = "";
-
-		switch (qs->action) {
-		case BLAME:
-			action = "blame";
-			break;
-		case BRIEFS:
-			action = "briefs";
-			break;
-		case COMMITS:
-			action = "commits";
-			break;
-		case DIFF:
-			action = "diff";
-			break;
-		case SUMMARY:
-			action = "summary";
-			break;
-		case TAG:
-			action = "tag";
-			break;
-		case TAGS:
-			action = "tags";
-			break;
-		case TREE:
-			action = "tree";
-			break;
-		}
-
-		if (fcgi_printf(c, " / %s", action) == -1)
-			goto done;
-	}
-
-	fcgi_printf(c, "</div>\n"	/* #site_path */
-	    "</div>\n"			/* #site_link */
-	    "<div id='content'>\n");
-
-done:
-	return NULL;
-}
-
-static const struct got_error *
-gotweb_render_footer(struct request *c)
-{
-	const struct got_error *error = NULL;
-	struct server *srv = c->srv;
-	const char *siteowner = "&nbsp;";
-	char *escaped_owner = NULL;
-
-	if (srv->show_site_owner) {
-		error = gotweb_escape_html(&escaped_owner, srv->site_owner);
-		if (error)
-			return error;
-		siteowner = escaped_owner;
-	}
-
-	fcgi_printf(c, "<div id='site_owner_wrapper'>\n"
-	    "<div id='site_owner'>%s</div>\n"
-	    "</div>\n"		/* #site_owner_wrapper */
-	    "</div>\n"		/* #content */
-	    "</div>\n"		/* #gw_body */
-	    "</body>\n</html>\n", siteowner);
-
-	free(escaped_owner);
-	return NULL;
-}
-
-static const struct got_error *
+const struct got_error *
 gotweb_render_navs(struct request *c)
 {
 	const struct got_error *error = NULL;
@@ -996,7 +845,7 @@ gotweb_render_index(struct request *c)
 	struct dirent **sd_dent = NULL;
 	unsigned int d_cnt, d_i, d_disp = 0;
 	unsigned int d_skipped = 0;
-	int r, type;
+	int type;
 
 	d = opendir(srv->repos_path);
 	if (d == NULL) {
@@ -1011,26 +860,9 @@ gotweb_render_index(struct request *c)
 		goto done;
 	}
 
-	r = fcgi_printf(c, "<div id='index_header'>\n"
-	    "<div id='index_header_project'>Project</div>\n");
-	if (r == -1)
+	if (gotweb_render_repo_table_hdr(c->tp) == -1)
 		goto done;
 
-	if (srv->show_repo_description)
-		if (fcgi_printf(c, "<div id='index_header_description'>"
-		    "Description</div>\n") == -1)
-			goto done;
-	if (srv->show_repo_owner)
-		if (fcgi_printf(c, "<div id='index_header_owner'>"
-		    "Owner</div>\n") == -1)
-			goto done;
-	if (srv->show_repo_age)
-		if (fcgi_printf(c, "<div id='index_header_age'>"
-		    "Last Change</div>\n") == -1)
-			goto done;
-	if (fcgi_printf(c, "</div>\n") == -1) /* #index_header */
-		goto done;
-
 	for (d_i = 0; d_i < d_cnt; d_i++) {
 		if (srv->max_repos > 0 && t->prev_disp == srv->max_repos)
 			break;
@@ -1074,112 +906,9 @@ gotweb_render_index(struct request *c)
 		d_disp++;
 		t->prev_disp++;
 
-		if (fcgi_printf(c, "<div class='index_wrapper'>\n"
-		    "<div class='index_project'>") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = SUMMARY,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name,
-		    }, "%s", repo_dir->name);
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, "</div>") == -1) /* .index_project */
-			goto done;
-
-		if (srv->show_repo_description) {
-			r = fcgi_printf(c,
-			    "<div class='index_project_description'>\n"
-			    "%s</div>\n", repo_dir->description);
-			if (r == -1)
-				goto done;
-		}
-
-		if (srv->show_repo_owner) {
-			r = fcgi_printf(c, "<div class='index_project_owner'>"
-			    "%s</div>\n", repo_dir->owner);
-			if (r == -1)
-				goto done;
-		}
-
-		if (srv->show_repo_age) {
-			r = fcgi_printf(c, "<div class='index_project_age'>"
-			    "%s</div>\n", repo_dir->age);
-			if (r == -1)
-				goto done;
-		}
-
-		if (fcgi_printf(c, "<div class='navs_wrapper'>"
-		    "<div class='navs'>") == -1)
+		if (gotweb_render_repo_fragment(c->tp, repo_dir) == -1)
 			goto done;
 
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = SUMMARY,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name
-		    }, "summary");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, " | ") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = BRIEFS,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name
-		    }, "commit briefs");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, " | ") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = COMMITS,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name
-		    }, "commits");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, " | ") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = TAGS,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name
-		    }, "tags");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, " | ") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = TREE,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name
-		    }, "tree");
-		if (r == -1)
-			goto done;
-
-		r = fcgi_printf(c, "</div>"	/* .navs */
-		    "<div class='dotted_line'></div>\n"
-		    "</div>\n"			/* .navs_wrapper */
-		    "</div>\n");		/* .index_wrapper */
-		if (r == -1)
-			goto done;
-
 		gotweb_free_repo_dir(repo_dir);
 		repo_dir = NULL;
 		t->next_disp++;
@@ -1264,143 +993,6 @@ done:
 }
 
 static const struct got_error *
-gotweb_render_briefs(struct request *c)
-{
-	const struct got_error *error = NULL;
-	struct repo_commit *rc = NULL;
-	struct server *srv = c->srv;
-	struct transport *t = c->t;
-	struct querystring *qs = t->qs;
-	struct repo_dir *repo_dir = t->repo_dir;
-	char *smallerthan, *newline;
-	char *age = NULL, *author = NULL, *msg = NULL;
-	int r;
-
-	r = fcgi_printf(c, "<div id='briefs_title_wrapper'>\n"
-	    "<div id='briefs_title'>Commit Briefs</div>\n"
-	    "</div>\n"	/* #briefs_title_wrapper */
-	    "<div id='briefs_content'>\n");
-	if (r == -1)
-		goto done;
-
-	if (qs->action == SUMMARY) {
-		qs->action = BRIEFS;
-		error = got_get_repo_commits(c, D_MAXSLCOMMDISP);
-	} else
-		error = got_get_repo_commits(c, srv->max_commits_display);
-	if (error)
-		goto done;
-
-	TAILQ_FOREACH(rc, &t->repo_commits, entry) {
-		error = gotweb_get_time_str(&age, rc->committer_time, TM_DIFF);
-		if (error)
-			goto done;
-
-		smallerthan = strchr(rc->author, '<');
-		if (smallerthan)
-			*smallerthan = '\0';
-
-		newline = strchr(rc->commit_msg, '\n');
-		if (newline)
-			*newline = '\0';
-
-		error = gotweb_escape_html(&author, rc->author);
-		if (error)
-			goto done;
-		error = gotweb_escape_html(&msg, rc->commit_msg);
-		if (error)
-			goto done;
-
-		r = fcgi_printf(c, "<div class='briefs_age'>%s</div>\n"
-		    "<div class='briefs_author'>%s</div>\n"
-		    "<div class='briefs_log'>",
-		    age, author);
-		if (r == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = DIFF,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name,
-			.commit = rc->commit_id,
-			.headref = qs->headref,
-		    }, "%s", msg);
-		if (r == -1)
-			goto done;
-
-		if (rc->refs_str) {
-			char *refs;
-
-			error = gotweb_escape_html(&refs, rc->refs_str);
-			if (error)
-				goto done;
-			r = fcgi_printf(c,
-			    " <span class='refs_str'>(%s)</span>", refs);
-			free(refs);
-			if (r == -1)
-				goto done;
-		}
-		if (fcgi_printf(c, "</div>\n") == -1) /* .briefs_log */
-			goto done;
-
-		r = fcgi_printf(c, "<div class='navs_wrapper'>\n"
-		    "<div class='navs'>");
-		if (r == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = DIFF,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name,
-			.commit = rc->commit_id,
-			.headref = qs->headref,
-		    }, "diff");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, " | ") == -1)
-			goto done;
-
-		r = gotweb_link(c, &(struct gotweb_url){
-			.action = TREE,
-			.index_page = -1,
-			.page = -1,
-			.path = repo_dir->name,
-			.commit = rc->commit_id,
-			.headref = qs->headref,
-		    }, "tree");
-		if (r == -1)
-			goto done;
-
-		if (fcgi_printf(c, "</div>\n"	/* .navs */
-		    "</div>\n"	/* .navs_wrapper */
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-
-		free(age);
-		age = NULL;
-		free(author);
-		author = NULL;
-		free(msg);
-		msg = NULL;
-	}
-
-	if (t->next_id || t->prev_id) {
-		error = gotweb_render_navs(c);
-		if (error)
-			goto done;
-	}
-	fcgi_printf(c, "</div>\n"); /* #briefs_content */
-done:
-	free(age);
-	free(author);
-	free(msg);
-	return error;
-}
-
-static const struct got_error *
 gotweb_render_commits(struct request *c)
 {
 	const struct got_error *error = NULL;
@@ -1811,11 +1403,8 @@ gotweb_render_summary(struct request *c)
 	if (r == -1)
 		goto done;
 
-	error = gotweb_render_briefs(c);
-	if (error) {
-		log_warnx("%s: %s", __func__, error->msg);
+	if (gotweb_render_briefs(c->tp) == -1)
 		goto done;
-	}
 
 	error = gotweb_render_tags(c);
 	if (error) {
@@ -2177,8 +1766,8 @@ gotweb_urlencode(const char *str)
 	return escaped;
 }
 
-static inline const char *
-action_name(int action)
+const char *
+gotweb_action_name(int action)
 {
 	switch (action) {
 	case BLAME:
@@ -2208,14 +1797,14 @@ action_name(int action)
 	}
 }
 
-static int
-gotweb_print_url(struct request *c, struct gotweb_url *url)
+int
+gotweb_render_url(struct request *c, struct gotweb_url *url)
 {
 	const char *sep = "?", *action;
 	char *tmp;
 	int r;
 
-	action = action_name(url->action);
+	action = gotweb_action_name(url->action);
 	if (action != NULL) {
 		if (fcgi_printf(c, "?action=%s", action) == -1)
 			return -1;
@@ -2309,7 +1898,7 @@ gotweb_link(struct request *c, struct gotweb_url *url,
 	if (fcgi_printf(c, "<a href='") == -1)
 		return -1;
 
-	if (gotweb_print_url(c, url) == -1)
+	if (gotweb_render_url(c, url) == -1)
 		return -1;
 
 	if (fcgi_printf(c, "'>") == -1)
blob - 14426b3f4ad9787e7876dc233211e8fb8875cab6
blob + 64fce7c6d8ee3f57305034f0c0434cff991a82bd
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -202,10 +202,12 @@ enum socket_priv_fds {
 	PRIV_FDS__MAX,
 };
 
+struct template;
 struct request {
 	struct socket			*sock;
 	struct server			*srv;
 	struct transport		*t;
+	struct template			*tp;
 	struct event			 ev;
 	struct event			 tmo;
 
@@ -415,6 +417,11 @@ enum query_actions {
 	ACTIONS__MAX,
 };
 
+enum gotweb_ref_tm {
+	TM_DIFF,
+	TM_LONG,
+};
+
 extern struct gotwebd	*gotwebd_env;
 
 /* sockets.c */
@@ -432,6 +439,8 @@ const struct got_error
 const struct got_error *gotweb_get_time_str(char **, time_t, int);
 const struct got_error *gotweb_init_transport(struct transport **);
 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_link(struct request *, struct gotweb_url *, const char *, ...)
 	__attribute__((__format__(printf, 3, 4)))
 	__attribute__((__nonnull__(3)));
@@ -440,6 +449,13 @@ void gotweb_free_repo_tag(struct repo_tag *);
 void gotweb_process_request(struct request *);
 void gotweb_free_transport(struct transport *);
 
+/* pages.tmpl */
+int	gotweb_render_header(struct template *);
+int	gotweb_render_footer(struct template *);
+int	gotweb_render_repo_table_hdr(struct template *);
+int	gotweb_render_repo_fragment(struct template *, struct repo_dir *);
+int	gotweb_render_briefs(struct template *);
+
 /* parse.y */
 int parse_config(const char *, struct gotwebd *);
 int cmdline_symset(char *);
@@ -450,6 +466,8 @@ void fcgi_timeout(int, short, void *);
 void fcgi_cleanup_request(struct request *);
 void fcgi_create_end_record(struct request *);
 void dump_fcgi_record(const char *, struct fcgi_record_header *);
+int fcgi_puts(struct template *, const char *);
+int fcgi_putc(struct template *, int);
 int fcgi_vprintf(struct request *, const char *, va_list);
 int fcgi_printf(struct request *, const char *, ...)
 	__attribute__((__format__(printf, 2, 3)))
blob - /dev/null
blob + 0d49898e174e9bfad77b158729eb04ab949bd736 (mode 644)
--- /dev/null
+++ gotwebd/pages.tmpl
@@ -0,0 +1,302 @@
+{!
+/*
+ * Copyright (c) 2022 Omar Polo <op@openbsd.org>
+ * Copyright (c) 2016, 2019, 2020-2022 Tracey Emery <tracey@traceyemery.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <event.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "proc.h"
+
+#include "gotwebd.h"
+#include "tmpl.h"
+
+const struct got_error	*gotweb_render_navs(struct request *);
+
+static int
+gotweb_render_age(struct template *tp, time_t time, int ref_tm)
+{
+	const struct got_error *err;
+	char *age;
+	int r;
+
+	err = gotweb_get_time_str(&age, time, ref_tm);
+	if (err)
+		return 0;
+	r = tp->tp_puts(tp, age);
+	free(age);
+	return r;
+}
+
+!}
+
+{{ define gotweb_render_header(struct template *tp) }}
+{!
+	struct request		*c = tp->tp_arg;
+	struct server		*srv = c->srv;
+	struct querystring	*qs = c->t->qs;
+	struct gotweb_url	 u_path;
+	const char		*prfx = c->script_name;
+	const char		*css = srv->custom_css;
+
+	memset(&u_path, 0, sizeof(u_path));
+	u_path.index_page = -1;
+	u_path.page = -1;
+	u_path.action = SUMMARY;
+!}
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>{{ srv->site_name }}</title>
+    <meta name="viewport" content="initial-scale=.75" />
+    <meta name="msapplication-TileColor" content="#da532c" />
+    <meta name="theme-color" content="#ffffff"/>
+    <link rel="apple-touch-icon" sizes="180x180" href="{{ prfx }}apple-touch-icon.png" />
+    <link rel="icon" type="image/png" sizes="32x32" href="{{ prfx }}favicon-32x32.png" />
+    <link rel="icon" type="image/png" sizes="16x16" href="{{ prfx }}favicon-16x16.png" />
+    <link rel="manifest" href="{{ prfx }}site.webmanifest"/>
+    <link rel="mask-icon" href="{{ prfx }}safari-pinned-tab.svg" />
+    <link rel="stylesheet" type="text/css" href="{{ prfx }}{{ css }}" />
+  </head>
+  <body>
+    <div id="gw_body">
+      <div id="header">
+        <div id="got_link">
+          <a href="{{ srv->logo_url }}" target="_blank">
+            <img src="{{ prfx }}{{ srv->logo }}" />
+          </a>
+        </div>
+      </div>
+      <div id="site_path">
+        <div id="site_link">
+          <a href="?index_page={{ printf "%d", qs->index_page }}">
+            {{ srv->site_link }}
+          </a>
+          {{ if qs->path }}
+            {! u_path.path = qs->path; !}
+            {{ " / " }}
+            <a href="{{ render gotweb_render_url(tp->tp_arg, &u_path)}}">
+              {{ qs->path }}
+            </a>
+          {{ end }}
+          {{ if qs->action != INDEX }}
+            {{ " / " }}{{ gotweb_action_name(qs->action) }}
+          {{ end }}
+        </div>
+      </div>
+      <div id="content">
+{{ end }}
+
+{{ define gotweb_render_footer(struct template *tp) }}
+{!
+	struct request		*c = tp->tp_arg;
+	struct server		*srv = c->srv;
+!}
+        <div id="site_owner_wrapper">
+          <div id="site_owner">
+            {{ if srv->show_site_owner }}
+              {{ srv->site_owner }}
+            {{ end }}
+          </div>
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
+{{ end }}
+
+{{ define gotweb_render_repo_table_hdr(struct template *tp) }}
+{!
+	struct request *c = tp->tp_arg;
+	struct server *srv = c->srv;
+!}
+<div id="index_header">
+  <div id="index_header_project">
+    Project
+  </div>
+  {{ if srv->show_repo_description }}
+    <div id="index_header_description">
+      Description
+    </div>
+  {{ end }}
+  {{ if srv->show_repo_owner }}
+    <div id="index_header_owner">
+      Owner
+    </div>
+  {{ end }}
+  {{ if srv->show_repo_age }}
+    <div id="index_header_age">
+      Last Change
+    </div>
+  {{ end }}
+</div>
+{{ end }}
+
+{{ define gotweb_render_repo_fragment(struct template *tp, struct repo_dir *repo_dir) }}
+{!
+	struct request *c = tp->tp_arg;
+	struct server *srv = c->srv;
+	struct gotweb_url summary = {
+		.action = SUMMARY,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+	}, briefs = {
+		.action = BRIEFS,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+	}, commits = {
+		.action = COMMITS,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+	}, tags = {
+		.action = TAGS,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+	}, tree = {
+		.action = TREE,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+	};
+!}
+<div class="index_wrapper">
+  <div class="index_project">
+    <a href="{{ render gotweb_render_url(tp->tp_arg, &summary) }}">{{ repo_dir->name }}</a>
+  </div>
+  {{ if srv->show_repo_description }}
+    <div class="index_project_description">
+      {{ repo_dir->description }}
+    </div>
+  {{ end }}
+  {{ if srv->show_repo_owner }}
+    <div class="index_project_owner">
+      {{ repo_dir->owner }}
+    </div>
+  {{ end }}
+  {{ if srv->show_repo_age }}
+    <div class="index_project_age">
+      {{ repo_dir->age }}
+    </div>
+  {{ end }}
+  <div class="navs_wrapper">
+    <div class="navs">
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &summary) }}">summary</a>
+      {{ " | " }}
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &briefs) }}">briefs</a>
+      {{ " | " }}
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &commits) }}">commits</a>
+      {{ " | " }}
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &tags) }}">tags</a>
+      {{ " | " }}
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &tree) }}">tree</a>
+    </div>
+    <div class="dotted_line"></div>
+  </div>
+</div>
+{{ end }}
+
+{{ define gotweb_render_briefs(struct template *tp) }}
+{!
+	const struct got_error	*error;
+	struct request		*c = tp->tp_arg;
+	struct server		*srv = c->srv;
+	struct transport	*t = c->t;
+	struct querystring	*qs = c->t->qs;
+	struct repo_commit	*rc;
+	struct repo_dir		*repo_dir = t->repo_dir;
+	struct gotweb_url	 diff_url, tree_url;
+	char			*tmp;
+
+	diff_url = (struct gotweb_url){
+		.action = DIFF,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+		.headref = qs->headref,
+	};
+	tree_url = (struct gotweb_url){
+		.action = TREE,
+		.index_page = -1,
+		.page = -1,
+		.path = repo_dir->name,
+		.headref = qs->headref,
+	};
+
+	if (qs->action == SUMMARY) {
+		qs->action = BRIEFS;
+		error = got_get_repo_commits(c, D_MAXSLCOMMDISP);
+	} else
+		error = got_get_repo_commits(c, srv->max_commits_display);
+	if (error)
+		return -1;
+!}
+<div id="briefs_title_wrapper">
+  <div id="briefs_title">Commit Briefs</div>
+</div>
+<div id="briefs_content">
+  {{ tailq-foreach rc &t->repo_commits entry }}
+    {!
+	diff_url.commit = rc->commit_id;
+	tree_url.commit = rc->commit_id;
+
+	tmp = strchr(rc->author, '<');
+	if (tmp)
+		*tmp = '\0';
+
+	tmp = strchr(rc->commit_msg, '\n');
+	if (tmp)
+		*tmp = '\0';
+    !}
+    <div class="briefs_age">
+      {{ render gotweb_render_age(tp, rc->committer_time, TM_DIFF) }}
+    </div>
+    <div class="briefs_author">
+      {{ rc->author }}
+    </div>
+    <div class="briefs_log">
+      <a href="{{ render gotweb_render_url(tp->tp_arg, &diff_url) }}">
+        {{ rc->commit_msg }}
+      </a>
+      {{ if rc->refs_str }}
+        {{ " " }} <span class="refs_str">({{ rc->refs_str }})</span>
+      {{ end }}
+      </a>
+    </div>
+    <div class="navs_wrapper">
+      <div class="navs">
+        <a href="{{ render gotweb_render_url(tp->tp_arg, &diff_url) }}">diff</a>
+	{{ " | " }}
+	<a href="{{ render gotweb_render_url(tp->tp_arg, &tree_url) }}">tree</a>
+      </div>
+    </div>
+    <div class="dotted_line"></div>
+  {{ end }}
+  {{ if t->next_id || t->prev_id }}
+    {! gotweb_render_navs(c); !}
+  {{ end }}
+</div>
+{{ end }}
blob - c0691c5575dca8c931af0e8e30d9e086b5a5bccc
blob + cfba5dfb0e7368b0b37738d2eb71b53d437db169
--- gotwebd/sockets.c
+++ gotwebd/sockets.c
@@ -55,6 +55,7 @@
 
 #include "proc.h"
 #include "gotwebd.h"
+#include "tmpl.h"
 
 #define SOCKS_BACKLOG 5
 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
@@ -613,9 +614,18 @@ sockets_socket_accept(int fd, short event, void *arg)
 
 	c = calloc(1, sizeof(struct request));
 	if (c == NULL) {
+		log_warn("%s", __func__);
+		close(s);
+		cgi_inflight--;
+		return;
+	}
+
+	c->tp = template(c, fcgi_puts, fcgi_putc);
+	if (c->tp == NULL) {
 		log_warn("%s", __func__);
 		close(s);
 		cgi_inflight--;
+		free(c);
 		return;
 	}