Commit Diff


commit - 789a106aad27ab1178f8b2420738e39aae6bdf10
commit + 8e5ae9acc3ce865d775ae67f8b5936c400ae07f3
blob - b130fd67c91acce9727918c7af0be8d4aa4787ba
blob + 71f274df0de9c93ab7a130bb6af6dee175c6e588
--- msearchd/msearchd.8
+++ msearchd/msearchd.8
@@ -11,6 +11,7 @@
 .Op Fl j Ar n
 .Op Fl p Ar path
 .Op Fl s Ar socket
+.Op Fl t Ar tmpldir
 .Op Fl u Ar user
 .Op Ar db
 .Sh DESCRIPTION
@@ -59,6 +60,10 @@ effectively disables the chroot.
 .It Fl s Ar socket
 Create an bind to the local socket at
 .Ar socket .
+.It Fl t Ar tmpldir
+Path to a directory containing the template files.
+.Pa /etc/gotmarc
+by default.
 .It Fl u Ar user
 Drop privileges to
 .Ar user
@@ -72,7 +77,20 @@ Multiple
 options increase the verbosity.
 .El
 .Sh FILES
-.Bl -tag -width /var/www/msearchd/mails.sqlite3 -compact
+.Bl -tag -width Ds
+.It Pa /etc/gotmarc/foot.html
+Template with the trailing part of the page.
+.It Pa /etc/gotmarc/head.html
+Template with the first part of the page.
+.Dv TITLE
+is replaced with
+.Dq Search .
+.It Pa /etc/gotmarc/search-header.html
+Template for the start of the search page.
+.It Pa /etc/gotmarc/search.html
+Template for the search form.
+.Dv QUERY
+is replaced with the search query.
 .It Pa /var/www/msearchd/mails.sqite3
 Default database.
 .It Pa /var/www/run/msearchd.sock
blob - 7e3e40336b56a84aae1c6b4e109acb0caf2f029b
blob + c12aad4bd38910e532fd426187111fb49e8c0cff
--- msearchd/msearchd.c
+++ msearchd/msearchd.c
@@ -37,6 +37,10 @@
 #define MSEARCHD_USER "www"
 #endif
 
+#ifndef MSEARCH_TMPL_DIR
+#define MSEARCH_TMPL_DIR "/etc/gotmarc"
+#endif
+
 #define MAX_CHILDREN 32
 
 int	debug;
@@ -44,6 +48,11 @@ int	verbose;
 int	children = 3;
 pid_t	pids[MAX_CHILDREN];
 
+const char	*tmpl_head;
+const char	*tmpl_search;
+const char	*tmpl_search_header;
+const char	*tmpl_foot;
+
 __dead void	srch_syslog_fatal(int, const char *, ...);
 __dead void	srch_syslog_fatalx(int, const char *, ...);
 void		srch_syslog_warn(const char *, ...);
@@ -87,6 +96,40 @@ sighdlr(int sig)
 	errno = save_errno;
 }
 
+static void
+load_tmpl(const char **ret, const char *dir, const char *name)
+{
+	FILE		*fp;
+	struct stat	 sb;
+	char		*t;
+	char		 path[PATH_MAX];
+	int		 r;
+
+	r = snprintf(path, sizeof(path), "%s/%s", dir, name);
+	if (r < 0 || (size_t)r >= sizeof(path))
+		fatalx("path too long: %s/%s", dir, name);
+
+	if ((fp = fopen(path, "r")) == NULL)
+		fatal("can't open %s", path);
+
+	if (fstat(fileno(fp), &sb) == -1)
+		fatal("fstat");
+
+	if (sb.st_size > SIZE_MAX)
+		fatal("file too big %s", path);
+
+	if ((t = malloc(sb.st_size + 1)) == NULL)
+		fatal("malloc");
+
+	if (fread(t, 1, sb.st_size, fp) != sb.st_size)
+		fatal("fread %s", path);
+
+	fclose(fp);
+
+	t[sb.st_size] = '\0';
+	*ret = t;
+}
+
 static int
 bind_socket(const char *path, struct passwd *pw)
 {
@@ -149,9 +192,9 @@ bind_socket(const char *path, struct passwd *pw)
 
 static pid_t
 start_child(const char *argv0, const char *root, const char *user,
-    const char *db, int debug, int verbose, int fd)
+    const char *db, const char *tmpl, int debug, int verbose, int fd)
 {
-	const char	*argv[11];
+	const char	*argv[13];
 	int		 argc = 0;
 	pid_t		 pid;
 
@@ -174,6 +217,7 @@ start_child(const char *argv0, const char *root, const
 	argv[argc++] = argv0;
 	argv[argc++] = "-S";
 	argv[argc++] = "-p"; argv[argc++] = root;
+	argv[argc++] = "-t"; argv[argc++] = tmpl;
 	argv[argc++] = "-u"; argv[argc++] = user;
 	if (debug)
 		argv[argc++] = "-d";
@@ -192,8 +236,8 @@ start_child(const char *argv0, const char *root, const
 static void __dead
 usage(void)
 {
-	fprintf(stderr,
-	    "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n",
+	fprintf(stderr, "usage: %s [-dv] [-j n] [-p path] [-s socket]"
+	    " [-t tmpldir] [-u user] [db]\n",
 	    getprogname());
 	exit(1);
 }
@@ -208,6 +252,7 @@ main(int argc, char **argv)
 	const char	*user = MSEARCHD_USER;
 	const char	*root = NULL;
 	const char	*db = MSEARCHD_DB;
+	const char	*tmpldir = MSEARCH_TMPL_DIR;
 	const char	*errstr, *cause, *argv0;
 	pid_t		 pid;
 	int		 ch, i, fd, ret, status, server = 0;
@@ -231,7 +276,7 @@ main(int argc, char **argv)
 	if ((argv0 = argv[0]) == NULL)
 		argv0 = "msearchd";
 
-	while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) {
+	while ((ch = getopt(argc, argv, "dj:p:Ss:t:u:v")) != -1) {
 		switch (ch) {
 		case 'd':
 			debug = 1;
@@ -251,6 +296,9 @@ main(int argc, char **argv)
 		case 's':
 			sock = optarg;
 			break;
+		case 't':
+			tmpldir = optarg;
+			break;
 		case 'u':
 			user = optarg;
 			break;
@@ -309,8 +357,8 @@ main(int argc, char **argv)
 
 			if ((d = dup(fd)) == -1)
 				fatalx("dup");
-			pids[i] = start_child(argv0, root, user, db, debug,
-			    verbose, d);
+			pids[i] = start_child(argv0, root, user, db, tmpldir,
+			    debug, verbose, d);
 			log_debug("forking child %d (pid %lld)", i,
 			    (long long)pids[i]);
 		}
@@ -321,6 +369,11 @@ main(int argc, char **argv)
 		signal(SIGHUP, SIG_IGN);
 
 		sigprocmask(SIG_UNBLOCK, &set, NULL);
+	} else {
+		load_tmpl(&tmpl_head, tmpldir, "head.html");
+		load_tmpl(&tmpl_search, tmpldir, "search.html");
+		load_tmpl(&tmpl_search_header, tmpldir, "search-header.html");
+		load_tmpl(&tmpl_foot, tmpldir, "foot.html");
 	}
 
 	if (chroot(root) == -1)
blob - 47503ed5c28b0c755cf05499e59e260a30933d5f
blob + e0ac0be5e89e31a03ddc36507791c0320adef3f9
--- msearchd/msearchd.h
+++ msearchd/msearchd.h
@@ -108,6 +108,12 @@ int	clt_printf(struct client *, const char *, ...)
 int	fcgi_cmp(struct fcgi *, struct fcgi *);
 int	fcgi_client_cmp(struct client *, struct client *);
 
+/* msearchd.c */
+extern const char	*tmpl_head;
+extern const char	*tmpl_search;
+extern const char	*tmpl_search_header;
+extern const char	*tmpl_foot;
+
 /* server.c */
 int	server_main(const char *);
 int	server_handle(struct env *, struct client *);
blob - cd75c40c656b6516755731f7714583f76ba56c51
blob + 9a533b7f961b04d3bd636c5dd42247025b4701c7
--- msearchd/server.c
+++ msearchd/server.c
@@ -272,6 +272,27 @@ fts_escape(const char *p, char *buf, size_t bufsize)
 	return (-1);
 }
 
+static int
+render_tmpl(struct client *clt, const char *tmpl,
+    const char *var, const char *val)
+{
+	const char	*t;
+	size_t		 vlen;
+
+	if (var == NULL)
+		return (clt_puts(clt, tmpl));
+
+	vlen = strlen(var);
+	while ((t = strstr(tmpl, var)) != NULL) {
+		if (clt_write(clt, tmpl, t - tmpl) == -1 ||
+		    clt_putsan(clt, val) == -1)
+			return (-1);
+		tmpl = t + vlen;
+	}
+
+	return (clt_puts(clt, tmpl));
+}
+
 int
 server_handle(struct env *env, struct client *clt)
 {
@@ -304,37 +325,11 @@ server_handle(struct env *env, struct client *clt)
 	if (server_reply(clt, 200, "text/html") == -1)
 		goto err;
 
-	if (clt_puts(clt, "<!doctype html>"
-	    "<html>"
-	    "<head>"
-	    "<meta charset='utf-8'>"
-	    "<meta name='viewport' content='width=device-width'>"
-	    "<link rel='stylesheet' href='/style.css'>"
-	    "<title>Game of Trees Mail Archive | Search</title>"
-	    "</head>"
-	    "<body>"
-	    "<header class='index-header'>"
-	    "<a href='https://gameoftrees.org' target='_blank'>"
-	    "<img src='/got.png' srcset='/got.png, /got@2x.png 2x'"
-	    "     alt='\"GOT\" where the \"O\" is a cute, smiling pufferfish'"
-	    "     />"
-	    "</a>"
-	    "<h1>Game of Trees Mail Archive</h1>"
-	    "</header>") == -1)
+	if (render_tmpl(clt, tmpl_head, "TITLE", "Search") == -1 ||
+	    render_tmpl(clt, tmpl_search_header, NULL, NULL) == -1 ||
+	    render_tmpl(clt, tmpl_search, "QUERY", query) == -1)
 		goto err;
 
-	if (clt_puts(clt, "<nav>"
-	    "<a href='/'>Index</a>"
-	    "</nav>"
-	    "<form method='get'>"
-	    "<label>Search: "
-	    "<input type='search' name='q' value='") == -1 ||
-	    clt_putsan(clt, query) == -1 ||
-	    clt_puts(clt, "'/></label>"
-	    " <button type='submit'>search</button>"
-	    "</form>") == -1)
-		goto err;
-
 	if (query == NULL)
 		goto done;
 
@@ -398,7 +393,7 @@ server_handle(struct env *env, struct client *clt)
 		goto err;
 
 done:
-	if (clt_puts(clt, "</body></html>\n") == -1)
+	if (render_tmpl(clt, tmpl_foot, NULL, NULL) == -1)
 		goto err;
 
 	sqlite3_reset(env->env_query);
blob - /dev/null
blob + b850ad407bec9a23b378dec5f925cf7bcaa8ec7a (mode 644)
--- /dev/null
+++ templates/search-header.html
@@ -0,0 +1,11 @@
+<header class='index-header'>
+  <a href="https://gameoftrees.org" target="_blank">
+    <img src='/got.png'
+         srcset='/got.png, /got@2x.png 2x'
+         alt='"GOT" where the "O" is a cute smiling pufferfish.' />
+  </a>
+  <h1>Game of Trees Mail Archive</h1>
+</header>
+<nav>
+  <a href='/'>Index</a>
+</nav>