commit - 789a106aad27ab1178f8b2420738e39aae6bdf10
commit + 8e5ae9acc3ce865d775ae67f8b5936c400ae07f3
blob - b130fd67c91acce9727918c7af0be8d4aa4787ba
blob + 71f274df0de9c93ab7a130bb6af6dee175c6e588
--- msearchd/msearchd.8
+++ msearchd/msearchd.8
.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
.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
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
#define MSEARCHD_USER "www"
#endif
+#ifndef MSEARCH_TMPL_DIR
+#define MSEARCH_TMPL_DIR "/etc/gotmarc"
+#endif
+
#define MAX_CHILDREN 32
int debug;
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 *, ...);
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)
{
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;
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";
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);
}
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;
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;
case 's':
sock = optarg;
break;
+ case 't':
+ tmpldir = optarg;
+ break;
case 'u':
user = optarg;
break;
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]);
}
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
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
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)
{
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;
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
+<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>