html-error: substitute template variables via a regex

previously, in order to detect and insert {variables} into error/stats
templates, tinyproxy iterated char-by-char over the input file, and would
try to parse anything inside {} pairs and treat it like a variable name.
this breaks CSS, and additionally it's dog slow as tinyproxy wrote every
single character to the client via a write syscall.
now we process line-by-line, and inspect all matches of the regex
\{[a-z]{1,32}\}. if the contents of the regex are a known variable name,
substitution is taking place. if not, the contents are passed as-is to
the client. also the chunks before and after matches are written in
a single syscall.

closes #108
This commit is contained in:
rofl0r 2020-09-07 04:32:13 +01:00
parent 17ae1b512c
commit a8848d4bd8

View File

@ -83,76 +83,49 @@ static char *get_html_file (unsigned int errornum)
return (val);
}
static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) {
int fd = connptr->client_fd;
while(*p) {
regmatch_t match;
char varname[32+1], *varval;
size_t l;
int st = regexec(re, p, 1, &match, 0);
if(st == 0) {
if(match.rm_so > 0) safe_write(fd, p, match.rm_so);
l = match.rm_eo - match.rm_so;
assert(l>2 && l-2 < sizeof(varname));
p += match.rm_so;
memcpy(varname, p+1, l-2);
varname[l-2] = 0;
varval = lookup_variable(connptr->error_variables, varname);
if(varval) write_message(fd, "%s", varval);
else if(varval && !*varval) write_message(fd, "(unknown)");
else safe_write(fd, p, l);
p += l;
} else {
write_message(fd, "%s", p);
break;
}
}
}
/*
* Send an already-opened file to the client with variable substitution.
*/
int
send_html_file (FILE *infile, struct conn_s *connptr)
{
char *inbuf;
char *varstart = NULL;
char *p;
const char *varval;
int in_variable = 0;
int r = 0;
regex_t re;
char *inbuf = safemalloc (4096);
(void) regcomp(&re, "{[a-z]\\{1,32\\}}", 0);
inbuf = (char *) safemalloc (4096);
while (fgets (inbuf, 4096, infile) != NULL) {
for (p = inbuf; *p; p++) {
switch (*p) {
case '}':
if (in_variable) {
*p = '\0';
varval = (const char *)
lookup_variable (connptr->error_variables,
varstart);
if (!varval || !varval[0])
varval = "(unknown)";
r = write_message (connptr->client_fd,
"%s", varval);
in_variable = 0;
} else {
r = write_message (connptr->client_fd,
"%c", *p);
}
break;
case '{':
/* a {{ will print a single {. If we are NOT
* already in a { variable, then proceed with
* setup. If we ARE already in a { variable,
* this code will fallthrough to the code that
* just dumps a character to the client fd.
*/
if (!in_variable) {
varstart = p + 1;
in_variable++;
} else
in_variable = 0;
/* FALL THROUGH */
default:
if (!in_variable) {
r = write_message (connptr->client_fd,
"%c", *p);
}
}
if (r)
break;
}
if (r)
break;
in_variable = 0;
while (fgets (inbuf, 4096, infile)) {
varsubst_sendline(connptr, &re, inbuf);
}
regfree (&re);
safefree (inbuf);
return r;
return 1;
}
int send_http_headers (struct conn_s *connptr, int code, const char *message)