ui-repolist: add ability to control what repos a user see

The auth-filter works at the repository level but still shows
repositories in ui-repolist. This change allows you to limit what
a user sees based on the STDOUT of a command.

The existing filter code doesn't handle the capture of stdout, so
a new mechanism was created just for this.

Signed-off-by: Andy Doan <andy.doan@linaro.org>
diff --git a/cgit.c b/cgit.c
index c4320f0..3a14622 100644
--- a/cgit.c
+++ b/cgit.c
@@ -227,6 +227,8 @@
 		ctx.cfg.owner_filter = cgit_new_filter(value, OWNER);
 	else if (!strcmp(name, "auth-filter"))
 		ctx.cfg.auth_filter = cgit_new_filter(value, AUTH);
+	else if (!strcmp(name, "allowed-repos-cmd"))
+		ctx.cfg.allowed_repos_cmd = xstrdup(expand_macros(value));
 	else if (!strcmp(name, "embedded"))
 		ctx.cfg.embedded = atoi(value);
 	else if (!strcmp(name, "max-atom-items"))
diff --git a/cgit.h b/cgit.h
index 7ec46b4..50e65f8 100644
--- a/cgit.h
+++ b/cgit.h
@@ -270,6 +270,7 @@
 	struct cgit_filter *email_filter;
 	struct cgit_filter *owner_filter;
 	struct cgit_filter *auth_filter;
+	char *allowed_repos_cmd;
 };
 
 struct cgit_page {
diff --git a/ui-repolist.c b/ui-repolist.c
index 9c0c47a..ce7c8cd 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -10,6 +10,14 @@
 #include "ui-repolist.h"
 #include "html.h"
 #include "ui-shared.h"
+#include "run-command.h"
+
+
+struct allowed_repos {
+	const char *name;
+	struct allowed_repos *next;
+};
+
 
 static time_t read_agefile(const char *path)
 {
@@ -97,6 +105,38 @@
 	return 0;
 }
 
+static struct allowed_repos* find_allowed_repos(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *argv[] = {ctx.cfg.allowed_repos_cmd, NULL};
+	char *result, *token;
+	struct allowed_repos *allowed = NULL, *prev=NULL;
+
+	cp.argv = argv;
+	cp.no_stdin = 1;
+
+	if (!capture_command(&cp, &buf, 1024)) {
+		strbuf_trim(&buf);
+		result = strbuf_detach(&buf, NULL);
+
+		token = strtok(result, "\n");
+		while( token != NULL ) {
+			if (!prev) {
+				allowed = xmalloc(sizeof(*allowed));
+				prev = allowed;
+			} else {
+				prev->next = xmalloc(sizeof(*allowed));
+				prev = prev->next;
+			}
+			prev->name = token;
+			prev->next = NULL;
+			token = strtok(NULL, "\n");
+		}
+	}
+	return allowed;
+}
+
 static int is_in_url(struct cgit_repo *repo)
 {
 	if (!ctx.qry.url)
@@ -106,21 +146,28 @@
 	return 0;
 }
 
-static int is_visible(struct cgit_repo *repo)
+static int is_visible(struct allowed_repos *allowed, struct cgit_repo *repo)
 {
 	if (repo->hide || repo->ignore)
 		return 0;
 	if (!(is_match(repo) && is_in_url(repo)))
 		return 0;
+	if (ctx.cfg.allowed_repos_cmd) {
+		while(allowed) {
+			if (!strcmp(repo->url, allowed->name))
+				return 1;
+			allowed = allowed->next;
+		}
+		return 0;
+	}
 	return 1;
 }
 
-static int any_repos_visible(void)
+static int any_repos_visible(struct allowed_repos *allowed)
 {
 	int i;
-
 	for (i = 0; i < cgit_repolist.count; i++) {
-		if (is_visible(&cgit_repolist.repos[i]))
+		if (is_visible(allowed, &cgit_repolist.repos[i]))
 			return 1;
 	}
 	return 0;
@@ -272,8 +319,12 @@
 	char *section;
 	char *repourl;
 	int sorted = 0;
+	struct allowed_repos * allowed = NULL;
 
-	if (!any_repos_visible()) {
+	if (ctx.cfg.allowed_repos_cmd)
+		allowed = find_allowed_repos();
+
+	if (!any_repos_visible(allowed)) {
 		cgit_print_error_page(404, "Not found", "No repositories found");
 		return;
 	}
@@ -296,7 +347,7 @@
 	html("<table summary='repository list' class='list nowrap'>");
 	for (i = 0; i < cgit_repolist.count; i++) {
 		ctx.repo = &cgit_repolist.repos[i];
-		if (!is_visible(ctx.repo))
+		if (!is_visible(allowed, ctx.repo))
 			continue;
 		hits++;
 		if (hits <= ctx.qry.ofs)