diff options
-rw-r--r-- | src/htmlerror.c | 242 | ||||
-rw-r--r-- | src/htmlerror.h | 20 |
2 files changed, 262 insertions, 0 deletions
diff --git a/src/htmlerror.c b/src/htmlerror.c new file mode 100644 index 0000000..aee92f5 --- /dev/null +++ b/src/htmlerror.c @@ -0,0 +1,242 @@ +/* $Id: htmlerror.c,v 1.1 2003-03-13 21:25:06 rjkaes Exp $ + * + * This file contains source code for the handling and display of + * HTML error pages with variable substitution. + */ +#include "tinyproxy.h" + +#include "common.h" +#include "buffer.h" +#include "conns.h" +#include "heap.h" +#include "htmlerror.h" +#include "network.h" +#include "utils.h" + +/* Add an error number -> filename mapping to the errorpages list. */ +int add_new_errorpage(char *filepath, unsigned int errornum) { + static int errorpage_count = 1; + + /* First, add space for another pointer to the errorpages array. */ + config.errorpages = saferealloc(config.errorpages, sizeof(struct error_pages_s *) * (errorpage_count + 1)); + if(!config.errorpages) + return(-1); + + /* Allocate space for an actual structure */ + config.errorpages[errorpage_count - 1] = safemalloc(sizeof(struct error_pages_s)); + if(!config.errorpages[errorpage_count - 1]) + return(-1); + + /* Set values for errorpage structure. */ + config.errorpages[errorpage_count - 1]->errorpage_path = safestrdup(filepath); + if(!config.errorpages[errorpage_count - 1]->errorpage_path) + return(-1); + + config.errorpages[errorpage_count - 1]->errorpage_errnum = errornum; + + /* Set NULL to denote end of array */ + config.errorpages[errorpage_count] = NULL; + + errorpage_count++; + return(0); +} + +/* Get the file appropriate for a given error. */ +char *get_html_file(int errornum) { + int i; + + if(!config.errorpages) return(config.errorpage_undef); + + for(i = 0; config.errorpages[i]; i++) { + if(config.errorpages[i]->errorpage_errnum == errornum) + return(config.errorpages[i]->errorpage_path); + } + + return(config.errorpage_undef); +} + +/* Look up the value for a variable. */ +char *lookup_variable(struct conn_s *connptr, char *varname) { + int i; + + for(i = 0; i<connptr->error_variable_count; i++) { + if(!strcasecmp(connptr->error_variables[i]->error_key, varname)) + return(connptr->error_variables[i]->error_val); + } + + return(NULL); +} + +#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++) { + 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; + break; + } + /* If we are not in a variable, then + * we fallthrough. the code for { will + * not do anything if in_variable is set to + * 0. it will end up in the default + * handler which will send the }. + */ + 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++; + } + 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, + 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 = \ + "<html><head><title>%s</title></head>" \ + "<body><blockquote><i>%s %s</i><br>" \ + "The page you requested was unavailable. The error code is listed " \ + "below. In addition, the HTML file which has been configured as the " \ + "page to be displayed when an error of this type was unavailable, " \ + "with the error code %d (%s). Please contact your administrator." \ + "<center>%s</center>" \ + "</body></html>" \ + "\r\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"))) + return(write_message(connptr->client_fd, fallback_error, + connptr->error_string, + PACKAGE, VERSION, + errno, strerror(errno), + connptr->error_string)); + + ret = send_html_file(infile, connptr); + fclose(infile); + return(ret); +} + +/* + * Add a key -> value mapping for HTML file substitution. + */ +int +add_error_variable(struct conn_s *connptr, char *key, char *val) +{ + connptr->error_variable_count++; + + /* Add space for a new pointer to the error_variables structure. */ + connptr->error_variables = saferealloc(connptr->error_variables, sizeof(struct error_variable_s *) * connptr->error_variable_count); + if(!connptr->error_variables) + return(-1); + + /* Allocate a new variable mapping structure. */ + connptr->error_variables[connptr->error_variable_count - 1] = safemalloc(sizeof(struct error_variable_s)); + if(!connptr->error_variables[connptr->error_variable_count - 1]) + return(-1); + + /* Set values for variable mapping. */ + connptr->error_variables[connptr->error_variable_count - 1]->error_key = safestrdup(key); + connptr->error_variables[connptr->error_variable_count - 1]->error_val = safestrdup(val); + if((!connptr->error_variables[connptr->error_variable_count - 1]->error_key) + || (!connptr->error_variables[connptr->error_variable_count - 1]->error_val)) + return(-1); + + return(0); +} + +#define ADD_VAR_RET(x, y) if(y) { if(add_error_variable(connptr, x, y) == -1) return(-1); } + +/* Set some standard variables used by all HTML pages */ +int add_standard_vars(struct conn_s *connptr) { + char timebuf[30]; + time_t global_time = time(NULL); + + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", + gmtime(&global_time)); + + ADD_VAR_RET("request", connptr->request_line); + ADD_VAR_RET("cause", connptr->error_string); + ADD_VAR_RET("clientip", connptr->client_ip_addr); + ADD_VAR_RET("clienthost", connptr->client_string_addr); + ADD_VAR_RET("version", VERSION); + ADD_VAR_RET("package", PACKAGE); + ADD_VAR_RET("date", timebuf); + 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) + return(-1); + } + + connptr->error_number = number; + connptr->error_string = safestrdup(message); + + return(add_standard_vars(connptr)); +} + diff --git a/src/htmlerror.h b/src/htmlerror.h new file mode 100644 index 0000000..705faaa --- /dev/null +++ b/src/htmlerror.h @@ -0,0 +1,20 @@ +/* $Id: htmlerror.h,v 1.1 2003-03-13 21:25:04 rjkaes Exp $ + * + * Contains header declarations for the HTML error functions in htmlerror.c + */ + +#ifndef TINYPROXY_HTMLERROR_H +#define TINYPROXY_HTMLERROR_H + +/* Forward declaration */ +struct conn_s; + +extern int add_new_errorpage(char *filepath, unsigned int errornum); +extern int send_http_error_message(struct conn_s *connptr); +extern int indicate_http_error(struct conn_s *connptr, int number, char *message, ...); +extern int add_error_variable(struct conn_s *connptr, char *key, char *val); +extern int send_html_file(FILE *infile, struct conn_s *connptr); +extern int send_http_headers(struct conn_s *connptr, int code, char *message); +extern int add_standard_vars(struct conn_s *connptr); + +#endif /* !TINYPROXY_HTMLERROR_H */ |