tinyproxy/src/html-error.c

314 lines
8.3 KiB
C
Raw Normal View History

/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2003 Steven Young <sdyoung@miranda.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* This file contains source code for the handling and display of
* HTML error pages with variable substitution.
*/
2009-08-06 15:12:53 -07:00
#include "main.h"
#include "common.h"
#include "buffer.h"
#include "conns.h"
#include "heap.h"
#include "html-error.h"
#include "network.h"
#include "utils.h"
/*
* Add an error number -> filename mapping to the errorpages list.
*/
2008-12-08 05:39:44 -08:00
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
#define ERRPAGES_BUCKETCOUNT 16
int
add_new_errorpage (char *filepath, unsigned int errornum)
{
char errornbuf[ERRORNUM_BUFSIZE];
config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
if (!config.errorpages)
return (-1);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
if (hashmap_insert (config.errorpages, errornbuf,
2008-12-08 05:39:44 -08:00
filepath, strlen (filepath) + 1) < 0)
return (-1);
return (0);
}
/*
* Get the file appropriate for a given error.
*/
static char *
get_html_file (unsigned int errornum)
{
hashmap_iter result_iter;
char errornbuf[ERRORNUM_BUFSIZE];
char *key;
static char *val;
assert (errornum >= 100 && errornum < 1000);
if (!config.errorpages)
return (config.errorpage_undef);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
result_iter = hashmap_find (config.errorpages, errornbuf);
if (hashmap_is_end (config.errorpages, result_iter))
return (config.errorpage_undef);
if (hashmap_return_entry (config.errorpages, result_iter,
2008-12-08 05:39:44 -08:00
&key, (void **) &val) < 0)
return (config.errorpage_undef);
return (val);
}
/*
* Look up the value for a variable.
*/
static char *
lookup_variable (struct conn_s *connptr, char *varname)
{
hashmap_iter result_iter;
char *key;
static char *data;
result_iter = hashmap_find (connptr->error_variables, varname);
if (hashmap_is_end (connptr->error_variables, result_iter))
return (NULL);
if (hashmap_return_entry (connptr->error_variables, result_iter,
2008-12-08 05:39:44 -08:00
&key, (void **) &data) < 0)
return (NULL);
return (data);
}
#define HTML_BUFSIZE 4096
/*
* Send an already-opened file to the client with variable substitution.
*/
int
send_html_file (FILE * infile, struct conn_s *connptr)
{
char inbuf[HTML_BUFSIZE], *varstart = NULL, *p;
char *varval;
int in_variable = 0, writeret;
while (fgets (inbuf, HTML_BUFSIZE, infile) != NULL)
{
for (p = inbuf; *p; p++)
2008-12-08 05:39:44 -08:00
{
switch (*p)
{
case '}':
if (in_variable)
{
*p = '\0';
if (!(varval = lookup_variable (connptr, varstart)))
varval = "(unknown)";
writeret = write_message (connptr->client_fd, "%s", varval);
if (writeret)
return (writeret);
in_variable = 0;
}
else
{
writeret = write_message (connptr->client_fd, "%c", *p);
if (writeret)
return (writeret);
}
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;
default:
if (!in_variable)
{
writeret = write_message (connptr->client_fd, "%c", *p);
if (writeret)
return (writeret);
}
}
}
in_variable = 0;
}
return (0);
}
int
send_http_headers (struct conn_s *connptr, int code, char *message)
{
char *headers =
"HTTP/1.0 %d %s\r\n"
"Server: %s/%s\r\n"
"Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n";
return (write_message (connptr->client_fd, headers,
2008-12-08 05:39:44 -08:00
code, message, PACKAGE, VERSION));
}
/*
* Display an error to the client.
*/
int
send_http_error_message (struct conn_s *connptr)
{
char *error_file;
FILE *infile;
int ret;
char *fallback_error =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
2008-12-08 04:26:08 -08:00
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
"\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
"<html>\n"
"<head><title>%d %s</title></head>\n"
"<body>\n"
"<h1>%s</h1>\n"
"<p>%s</p>\n"
"<hr />\n"
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n" "</html>\n";
send_http_headers (connptr, connptr->error_number, connptr->error_string);
error_file = get_html_file (connptr->error_number);
if (!(infile = fopen (error_file, "r")))
{
char *detail = lookup_variable (connptr, "detail");
return (write_message (connptr->client_fd, fallback_error,
2008-12-08 05:39:44 -08:00
connptr->error_number,
connptr->error_string,
connptr->error_string,
detail, PACKAGE, VERSION));
}
ret = send_html_file (infile, connptr);
fclose (infile);
return (ret);
}
/*
* Add a key -> value mapping for HTML file substitution.
*/
#define ERRVAR_BUCKETCOUNT 16
int
add_error_variable (struct conn_s *connptr, char *key, char *val)
{
if (!connptr->error_variables)
if (!(connptr->error_variables = hashmap_create (ERRVAR_BUCKETCOUNT)))
return (-1);
return hashmap_insert (connptr->error_variables, key, val,
2008-12-08 05:39:44 -08:00
strlen (val) + 1);
}
#define ADD_VAR_RET(x, y) \
do { \
if (y == NULL) \
break; \
if (add_error_variable(connptr, x, y) < 0) \
return -1; \
} while (0)
/*
* Set some standard variables used by all HTML pages
*/
int
add_standard_vars (struct conn_s *connptr)
{
char errnobuf[16];
char timebuf[30];
time_t global_time;
snprintf (errnobuf, sizeof errnobuf, "%d", connptr->error_number);
ADD_VAR_RET ("errno", errnobuf);
ADD_VAR_RET ("cause", connptr->error_string);
ADD_VAR_RET ("request", connptr->request_line);
ADD_VAR_RET ("clientip", connptr->client_ip_addr);
ADD_VAR_RET ("clienthost", connptr->client_string_addr);
/* The following value parts are all non-NULL and will
* trigger warnings in ADD_VAR_RET(), so we use
* add_error_variable() directly.
*/
global_time = time (NULL);
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
2008-12-08 05:39:44 -08:00
gmtime (&global_time));
add_error_variable (connptr, "date", timebuf);
2008-12-08 04:41:44 -08:00
add_error_variable (connptr, "website", "http://www.banu.com/tinyproxy/");
add_error_variable (connptr, "version", VERSION);
add_error_variable (connptr, "package", PACKAGE);
return (0);
}
/*
* Add the error information to the conn structure.
*/
int
indicate_http_error (struct conn_s *connptr, int number, char *message, ...)
{
va_list ap;
char *key, *val;
va_start (ap, message);
while ((key = va_arg (ap, char *)))
{
val = va_arg (ap, char *);
if (add_error_variable (connptr, key, val) == -1)
2008-12-08 05:39:44 -08:00
{
va_end (ap);
return (-1);
}
}
connptr->error_number = number;
connptr->error_string = safestrdup (message);
va_end (ap);
return (add_standard_vars (connptr));
}