X-Git-Url: http://www.privoxy.org/gitweb/?a=blobdiff_plain;f=cgi.c;h=2e9e2656f40ad7689535bc134d4fec09cc1bf192;hb=1cca8d974c645ef98cf343cb4dbf1ce42d5e88bd;hp=96af186c014d28f5010b080587f94d385cdade7e;hpb=22fdfb820f45adb31eb69b0b08e628c3512902d9;p=privoxy.git
diff --git a/cgi.c b/cgi.c
index 96af186c..2e9e2656 100644
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,3 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.131 2011/06/23 14:01:01 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -7,18 +6,15 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.131 2011/06/23 14:01:01 fabiankeil Exp $"
* html or gif answers, and to compose HTTP resonses.
* This only contains the framework functions, the
* actual handler functions are declared elsewhere.
- *
- * Functions declared include:
- *
*
- * Copyright : Written by and Copyright (C) 2001-2004, 2006-2008
- * the SourceForge Privoxy team. http://www.privoxy.org/
+ * Copyright : Written by and Copyright (C) 2001-2017
+ * members of the Privoxy team. http://www.privoxy.org/
*
* Based on the Internet Junkbuster originally written
- * by and Copyright (C) 1997 Anonymous Coders and
+ * by and Copyright (C) 1997 Anonymous Coders and
* Junkbusters Corporation. http://www.junkbusters.com
*
- * This program is free software; you can redistribute it
+ * 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
@@ -72,8 +68,6 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.131 2011/06/23 14:01:01 fabiankeil Exp $"
/* jcc.h is for mutex semaphore globals only */
#include "jcc.h"
-const char cgi_h_rcs[] = CGI_H_VERSION;
-
/*
* List of CGI functions: name, handler, description
* Note: Do NOT use single quotes in the description;
@@ -85,126 +79,139 @@ static const struct cgi_dispatcher cgi_dispatchers[] = {
"Privoxy main page",
TRUE },
#ifdef FEATURE_GRACEFUL_TERMINATION
- { "die",
- cgi_die,
+ { "die",
+ cgi_die,
"Shut down - Do not deploy this build in a production environment, "
"this is a one click Denial Of Service attack!!!",
- FALSE },
+ FALSE },
#endif
- { "show-status",
- cgi_show_status,
+ { "show-status",
+ cgi_show_status,
#ifdef FEATURE_CGI_EDIT_ACTIONS
"View & change the current configuration",
#else
"View the current configuration",
#endif
- TRUE },
- { "show-version",
- cgi_show_version,
- "View the source code version numbers",
- TRUE },
- { "show-request",
- cgi_show_request,
+ TRUE },
+#ifdef FEATURE_CLIENT_TAGS
+ /*
+ * This is marked as harmless because despite the description
+ * used in the menu the actual toggling is done through another
+ * path ("/toggle-client-tag").
+ */
+ { "client-tags",
+ cgi_show_client_tags,
+ "View or toggle the tags that can be set based on the clients address",
+ TRUE },
+#endif
+ { "show-request",
+ cgi_show_request,
"View the request headers",
- TRUE },
+ TRUE },
{ "show-url-info",
- cgi_show_url_info,
+ cgi_show_url_info,
"Look up which actions apply to a URL and why",
TRUE },
#ifdef FEATURE_TOGGLE
{ "toggle",
- cgi_toggle,
+ cgi_toggle,
"Toggle Privoxy on or off",
FALSE },
#endif /* def FEATURE_TOGGLE */
+#ifdef FEATURE_CLIENT_TAGS
+ { "toggle-client-tag",
+ cgi_toggle_client_tag,
+ NULL,
+ FALSE },
+#endif
#ifdef FEATURE_CGI_EDIT_ACTIONS
{ "edit-actions", /* Edit the actions list */
- cgi_edit_actions,
+ cgi_edit_actions,
NULL, FALSE },
{ "eaa", /* Shortcut for edit-actions-add-url-form */
- cgi_edit_actions_add_url_form,
+ cgi_edit_actions_add_url_form,
NULL, FALSE },
{ "eau", /* Shortcut for edit-actions-url-form */
- cgi_edit_actions_url_form,
+ cgi_edit_actions_url_form,
NULL, FALSE },
{ "ear", /* Shortcut for edit-actions-remove-url-form */
- cgi_edit_actions_remove_url_form,
+ cgi_edit_actions_remove_url_form,
NULL, FALSE },
{ "eal", /* Shortcut for edit-actions-list */
- cgi_edit_actions_list,
+ cgi_edit_actions_list,
NULL, FALSE },
{ "eafu", /* Shortcut for edit-actions-for-url */
- cgi_edit_actions_for_url,
+ cgi_edit_actions_for_url,
NULL, FALSE },
{ "eas", /* Shortcut for edit-actions-submit */
- cgi_edit_actions_submit,
+ cgi_edit_actions_submit,
NULL, FALSE },
{ "easa", /* Shortcut for edit-actions-section-add */
- cgi_edit_actions_section_add,
+ cgi_edit_actions_section_add,
NULL, FALSE },
{ "easr", /* Shortcut for edit-actions-section-remove */
- cgi_edit_actions_section_remove,
+ cgi_edit_actions_section_remove,
NULL, FALSE },
{ "eass", /* Shortcut for edit-actions-section-swap */
- cgi_edit_actions_section_swap,
+ cgi_edit_actions_section_swap,
NULL, FALSE },
{ "edit-actions-for-url",
- cgi_edit_actions_for_url,
+ cgi_edit_actions_for_url,
NULL, FALSE /* Edit the actions for (a) specified URL(s) */ },
{ "edit-actions-list",
- cgi_edit_actions_list,
+ cgi_edit_actions_list,
NULL, TRUE /* Edit the actions list */ },
{ "edit-actions-submit",
- cgi_edit_actions_submit,
+ cgi_edit_actions_submit,
NULL, FALSE /* Change the actions for (a) specified URL(s) */ },
{ "edit-actions-url",
- cgi_edit_actions_url,
+ cgi_edit_actions_url,
NULL, FALSE /* Change a URL pattern in the actionsfile */ },
{ "edit-actions-url-form",
- cgi_edit_actions_url_form,
+ cgi_edit_actions_url_form,
NULL, FALSE /* Form to change a URL pattern in the actionsfile */ },
{ "edit-actions-add-url",
- cgi_edit_actions_add_url,
+ cgi_edit_actions_add_url,
NULL, FALSE /* Add a URL pattern to the actionsfile */ },
{ "edit-actions-add-url-form",
- cgi_edit_actions_add_url_form,
+ cgi_edit_actions_add_url_form,
NULL, FALSE /* Form to add a URL pattern to the actionsfile */ },
{ "edit-actions-remove-url",
- cgi_edit_actions_remove_url,
+ cgi_edit_actions_remove_url,
NULL, FALSE /* Remove a URL pattern from the actionsfile */ },
{ "edit-actions-remove-url-form",
- cgi_edit_actions_remove_url_form,
+ cgi_edit_actions_remove_url_form,
NULL, FALSE /* Form to remove a URL pattern from the actionsfile */ },
{ "edit-actions-section-add",
- cgi_edit_actions_section_add,
+ cgi_edit_actions_section_add,
NULL, FALSE /* Remove a section from the actionsfile */ },
{ "edit-actions-section-remove",
- cgi_edit_actions_section_remove,
+ cgi_edit_actions_section_remove,
NULL, FALSE /* Remove a section from the actionsfile */ },
{ "edit-actions-section-swap",
- cgi_edit_actions_section_swap,
+ cgi_edit_actions_section_swap,
NULL, FALSE /* Swap two sections in the actionsfile */ },
#endif /* def FEATURE_CGI_EDIT_ACTIONS */
- { "error-favicon.ico",
- cgi_send_error_favicon,
+ { "error-favicon.ico",
+ cgi_send_error_favicon,
NULL, TRUE /* Sends the favicon image for error pages. */ },
- { "favicon.ico",
- cgi_send_default_favicon,
+ { "favicon.ico",
+ cgi_send_default_favicon,
NULL, TRUE /* Sends the default favicon image. */ },
- { "robots.txt",
- cgi_robots_txt,
- NULL, TRUE /* Sends a robots.txt file to tell robots to go away. */ },
+ { "robots.txt",
+ cgi_robots_txt,
+ NULL, TRUE /* Sends a robots.txt file to tell robots to go away. */ },
{ "send-banner",
- cgi_send_banner,
+ cgi_send_banner,
NULL, TRUE /* Send a built-in image */ },
{ "send-stylesheet",
- cgi_send_stylesheet,
+ cgi_send_stylesheet,
NULL, FALSE /* Send templates/cgi-style.css */ },
{ "t",
- cgi_transparent_image,
+ cgi_transparent_image,
NULL, TRUE /* Send a transparent image (short name) */ },
{ "url-info-osd.xml",
- cgi_send_url_info_osd,
+ cgi_send_url_info_osd,
NULL, TRUE /* Send templates/url-info-osd.xml */ },
{ "user-manual",
cgi_send_user_manual,
@@ -271,6 +278,13 @@ const char image_blank_data[] =
const size_t image_pattern_length = sizeof(image_pattern_data) - 1;
const size_t image_blank_length = sizeof(image_blank_data) - 1;
+#ifdef FEATURE_COMPRESSION
+/*
+ * Minimum length which a buffer has to reach before
+ * we bother to (re-)compress it. Completely arbitrary.
+ */
+const size_t LOWER_LENGTH_LIMIT_FOR_COMPRESSION = 1024U;
+#endif
static struct http_response cgi_error_memory_response[1];
@@ -280,7 +294,7 @@ static struct map *parse_cgi_parameters(char *argstring);
/*********************************************************************
- *
+ *
* Function : dispatch_cgi
*
* Description : Checks if a request URL has either the magical
@@ -310,15 +324,15 @@ struct http_response *dispatch_cgi(struct client_state *csp)
/* Either the host matches CGI_SITE_1_HOST ..*/
if ( ( (0 == strcmpic(host, CGI_SITE_1_HOST))
|| (0 == strcmpic(host, CGI_SITE_1_HOST ".")))
- && (path[0] == '/') )
+ && (path[0] == '/'))
{
/* ..then the path will all be for us. Remove leading '/' */
path++;
}
/* Or it's the host part CGI_SITE_2_HOST, and the path CGI_SITE_2_PATH */
- else if ( ( (0 == strcmpic(host, CGI_SITE_2_HOST ))
- || (0 == strcmpic(host, CGI_SITE_2_HOST ".")) )
- && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))) )
+ else if (( (0 == strcmpic(host, CGI_SITE_2_HOST))
+ || (0 == strcmpic(host, CGI_SITE_2_HOST ".")))
+ && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))))
{
/* take everything following CGI_SITE_2_PATH */
path += strlen(CGI_SITE_2_PATH);
@@ -342,7 +356,22 @@ struct http_response *dispatch_cgi(struct client_state *csp)
return NULL;
}
- /*
+ if (strcmpic(csp->http->gpc, "GET")
+ && strcmpic(csp->http->gpc, "HEAD"))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "CGI request with unsupported method received: %s", csp->http->gpc);
+ /*
+ * The CGI pages currently only support GET and HEAD requests.
+ *
+ * If the client used a different method, ditch any data following
+ * the current headers to reduce the likelihood of parse errors
+ * with the following request.
+ */
+ csp->client_iob->eod = csp->client_iob->cur;
+ }
+
+ /*
* This is a CGI call.
*/
@@ -387,7 +416,7 @@ static char *grep_cgi_referrer(const struct client_state *csp)
/*********************************************************************
- *
+ *
* Function : referrer_is_safe
*
* Description : Decides whether we trust the Referer for
@@ -405,6 +434,7 @@ static int referrer_is_safe(const struct client_state *csp)
{
char *referrer;
static const char alternative_prefix[] = "http://" CGI_SITE_1_HOST "/";
+ const char *trusted_cgi_referrer = csp->config->trusted_cgi_referrer;
referrer = grep_cgi_referrer(csp);
@@ -423,6 +453,18 @@ static int referrer_is_safe(const struct client_state *csp)
return TRUE;
}
+ else if ((trusted_cgi_referrer != NULL) && (0 == strncmp(referrer,
+ trusted_cgi_referrer, strlen(trusted_cgi_referrer))))
+ {
+ /*
+ * After some more testing this block should be merged with
+ * the previous one or the log level should bedowngraded.
+ */
+ log_error(LOG_LEVEL_INFO, "Granting access to %s based on trusted referrer %s",
+ csp->http->url, referrer);
+
+ return TRUE;
+ }
else
{
/* Untrustworthy referrer */
@@ -435,7 +477,7 @@ static int referrer_is_safe(const struct client_state *csp)
}
/*********************************************************************
- *
+ *
* Function : dispatch_known_cgi
*
* Description : Processes a CGI once dispatch_cgi has determined that
@@ -473,12 +515,15 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
{
query_args_start++;
}
- if (*query_args_start == '/')
+ if (*query_args_start == '/')
{
*query_args_start++ = '\0';
- if ((param_list = new_map()))
- {
- map(param_list, "file", 1, url_decode(query_args_start), 0);
+ param_list = new_map();
+ err = map(param_list, "file", 1, url_decode(query_args_start), 0);
+ if (JB_ERR_OK != err) {
+ free(param_list);
+ free(path_copy);
+ return cgi_error_memory();
}
}
else
@@ -508,7 +553,7 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
return cgi_error_memory();
}
- /*
+ /*
* Find and start the right CGI function
*/
d = cgi_dispatchers;
@@ -551,7 +596,9 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
if (err && (err != JB_ERR_MEMORY))
{
/* Unexpected error! Shouldn't get here */
- log_error(LOG_LEVEL_ERROR, "Unexpected CGI error %d in top-level handler. Please file a bug report!", err);
+ log_error(LOG_LEVEL_ERROR,
+ "Unexpected CGI error %d in top-level handler. "
+ "Please file a bug report!", err);
err = cgi_error_unknown(csp, rsp, err);
}
if (!err)
@@ -570,8 +617,8 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
d++;
}
}
-
-
+
+
/*********************************************************************
*
* Function : parse_cgi_parameters
@@ -588,16 +635,31 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
static struct map *parse_cgi_parameters(char *argstring)
{
char *p;
- char *vector[BUFFER_SIZE];
+ char **vector;
int pairs, i;
struct map *cgi_params;
- if (NULL == (cgi_params = new_map()))
+ /*
+ * XXX: This estimate is guaranteed to be high enough as we
+ * let ssplit() ignore empty fields, but also a bit wasteful.
+ * The same hack is used in get_last_url() so it looks like
+ * a real solution is needed.
+ */
+ size_t max_segments = strlen(argstring) / 2;
+ if (max_segments == 0)
{
- return NULL;
+ /*
+ * XXX: If the argstring is empty, there's really
+ * no point in creating a param list, but currently
+ * other parts of Privoxy depend on the list's existence.
+ */
+ max_segments = 1;
}
+ vector = malloc_or_die(max_segments * sizeof(char *));
- /*
+ cgi_params = new_map();
+
+ /*
* IE 5 does, of course, violate RFC 2316 Sect 4.1 and sends
* the fragment identifier along with the request, so we must
* cut it off here, so it won't pollute the CGI params:
@@ -607,7 +669,14 @@ static struct map *parse_cgi_parameters(char *argstring)
*p = '\0';
}
- pairs = ssplit(argstring, "&", vector, SZ(vector), 1, 1);
+ pairs = ssplit(argstring, "&", vector, max_segments);
+ assert(pairs != -1);
+ if (pairs == -1)
+ {
+ freez(vector);
+ free_map(cgi_params);
+ return NULL;
+ }
for (i = 0; i < pairs; i++)
{
@@ -616,12 +685,15 @@ static struct map *parse_cgi_parameters(char *argstring)
*p = '\0';
if (map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0))
{
+ freez(vector);
free_map(cgi_params);
return NULL;
}
}
}
+ freez(vector);
+
return cgi_params;
}
@@ -717,8 +789,8 @@ jb_err get_string_param(const struct map *parameters,
s = param;
while ((ch = *s++) != '\0')
{
- if ( ((unsigned char)ch < (unsigned char)' ')
- || (ch == '}') )
+ if (((unsigned char)ch < (unsigned char)' ')
+ || (ch == '}'))
{
/* Probable hack attempt, or user accidentally used '}'. */
return JB_ERR_CGI_PARAMS;
@@ -758,15 +830,14 @@ jb_err get_number_param(struct client_state *csp,
unsigned *pvalue)
{
const char *param;
- char ch;
- unsigned value;
+ char *endptr;
assert(csp);
assert(parameters);
assert(name);
assert(pvalue);
- *pvalue = 0;
+ *pvalue = 0;
param = lookup(parameters, name);
if (!*param)
@@ -774,36 +845,12 @@ jb_err get_number_param(struct client_state *csp,
return JB_ERR_CGI_PARAMS;
}
- /* We don't use atoi because I want to check this carefully... */
-
- value = 0;
- while ((ch = *param++) != '\0')
+ *pvalue = (unsigned int)strtol(param, &endptr, 0);
+ if (*endptr != '\0')
{
- if ((ch < '0') || (ch > '9'))
- {
- return JB_ERR_CGI_PARAMS;
- }
-
- ch = (char)(ch - '0');
-
- /* Note:
- *
- * Privoxy ran out of memory while processing your request. Please contact your proxy administrator, or try again later Privoxy ran out of memory while processing your request. Please contact your proxy administrator, or try again later Privoxy encountered an error while processing your request: Privoxy encountered an error while processing your request: Could not load template file Please contact your proxy administrator.500 Internal Privoxy Error
\r\n"
- "500 Internal Privoxy Error
\n"
+ "500 Internal Privoxy Error
\r\n"
- "500 Internal Privoxy Error
\n"
+ "";
static const char body_suffix[] =
- "
or one of its included components.
Please contact your proxy administrator.
\n" "If you are the proxy administrator, please put the required file(s)"
"in the (confdir)/templates
directory. The "
"location of the (confdir)
directory "
@@ -1129,9 +1184,9 @@ jb_err cgi_error_no_template(const struct client_state *csp,
#ifndef _WIN32
", or /etc/privoxy/
"
#endif /* ndef _WIN32 */
- ").
Privoxy encountered an error while processing your request:
\r\n" + "\n" + "\n" + "Privoxy encountered an error while processing your request:
\n" "Unexpected internal error: "; static const char body_suffix[] = - "
\r\n" + "\n" "Please " "" - "file a bug report.
\r\n" - "\r\n" - "\r\n"; + "file a bug report.\n" + "\n" + "\n"; /* Includes room for larger error numbers in the future. */ const size_t body_size = sizeof(body_prefix) + sizeof(body_suffix) + 5; assert(csp); @@ -1226,11 +1277,7 @@ jb_err cgi_error_unknown(const struct client_state *csp, rsp->is_static = 0; rsp->crunch_reason = INTERNAL_ERROR; - rsp->body = malloc(body_size); - if (rsp->body == NULL) - { - return JB_ERR_MEMORY; - } + rsp->body = malloc_or_die(body_size); snprintf(rsp->body, body_size, "%s%d%s", body_prefix, error_to_report, body_suffix); @@ -1250,7 +1297,7 @@ jb_err cgi_error_unknown(const struct client_state *csp, * * Description : CGI function that is called if the parameters * (query string) for a CGI were wrong. - * + * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * 2 : rsp = http_response data structure for output @@ -1258,7 +1305,7 @@ jb_err cgi_error_unknown(const struct client_state *csp, * CGI Parameters : none * * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory error. + * JB_ERR_MEMORY on out-of-memory error. * *********************************************************************/ jb_err cgi_error_bad_param(const struct client_state *csp, @@ -1280,7 +1327,7 @@ jb_err cgi_error_bad_param(const struct client_state *csp, /********************************************************************* * - * Function : cgi_redirect + * Function : cgi_redirect * * Description : CGI support function to generate a HTTP redirect * message @@ -1292,7 +1339,7 @@ jb_err cgi_error_bad_param(const struct client_state *csp, * CGI Parameters : None * * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory error. + * JB_ERR_MEMORY on out-of-memory error. * *********************************************************************/ jb_err cgi_redirect (struct http_response * rsp, const char *target) @@ -1327,8 +1374,8 @@ jb_err cgi_redirect (struct http_response * rsp, const char *target) * FIXME: I currently only work for actions, and would * like to be generalized for other topics. * - * Parameters : - * 1 : item = item (will NOT be free()d.) + * Parameters : + * 1 : item = item (will NOT be free()d.) * It is assumed to be HTML-safe. * 2 : config = The current configuration. * @@ -1373,7 +1420,7 @@ char *add_help_link(const char *item, * HTTP header - e.g.: * "Sun, 06 Nov 1994 08:49:37 GMT" * - * Parameters : + * Parameters : * 1 : time_offset = Time returned will be current time * plus this number of seconds. * 2 : buf = Destination for result. @@ -1386,12 +1433,6 @@ char *add_help_link(const char *item, *********************************************************************/ void get_http_time(int time_offset, char *buf, size_t buffer_size) { - static const char day_names[7][4] = - { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char month_names[12][4] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - struct tm *t; time_t current_time; #if defined(HAVE_GMTIME_R) @@ -1416,17 +1457,7 @@ void get_http_time(int time_offset, char *buf, size_t buffer_size) t = gmtime(¤t_time); #endif - /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */ - snprintf(buf, buffer_size, - "%s, %02d %s %4d %02d:%02d:%02d GMT", - day_names[t->tm_wday], - t->tm_mday, - month_names[t->tm_mon], - t->tm_year + 1900, - t->tm_hour, - t->tm_min, - t->tm_sec - ); + strftime(buf, buffer_size, "%a, %d %b %Y %H:%M:%S GMT", t); } @@ -1486,42 +1517,41 @@ static void get_locale_time(char *buf, size_t buffer_size) * Allocates a new buffer for the result, free'ing it is * up to the caller. * - * XXX: We should add a config option for the - * compression level. - * - * * Parameters : * 1 : buffer = buffer whose content should be compressed * 2 : buffer_length = length of the buffer + * 3 : compression_level = compression level for compress2() * * Returns : NULL on error, otherwise a pointer to the compressed * content of the input buffer. * *********************************************************************/ -char *compress_buffer(char *buffer, size_t *buffer_length) +char *compress_buffer(char *buffer, size_t *buffer_length, int compression_level) { char *compressed_buffer; - size_t new_length = *buffer_length; + uLongf new_length; + assert(-1 <= compression_level && compression_level <= 9); - compressed_buffer = malloc(new_length); - if (NULL == compressed_buffer) - { - log_error(LOG_LEVEL_FATAL, - "Out of memory allocation compression buffer."); - } + /* Let zlib figure out the maximum length of the compressed data */ + new_length = compressBound((uLongf)*buffer_length); + + compressed_buffer = malloc_or_die(new_length); if (Z_OK != compress2((Bytef *)compressed_buffer, &new_length, - (Bytef *)buffer, *buffer_length, Z_DEFAULT_COMPRESSION)) + (Bytef *)buffer, *buffer_length, compression_level)) { - log_error(LOG_LEVEL_ERROR, "Error in compress2()"); + log_error(LOG_LEVEL_ERROR, + "compress2() failed. Buffer size: %d, compression level: %d.", + new_length, compression_level); freez(compressed_buffer); return NULL; } log_error(LOG_LEVEL_RE_FILTER, - "Compressed content from %d to %d bytes.", *buffer_length, new_length); + "Compressed content from %d to %d bytes. Compression level: %d", + *buffer_length, new_length, compression_level); - *buffer_length = new_length; + *buffer_length = (size_t)new_length; return compressed_buffer; @@ -1545,7 +1575,7 @@ char *compress_buffer(char *buffer, size_t *buffer_length) * On error, free()s rsp and returns cgi_error_memory() * *********************************************************************/ -struct http_response *finish_http_response(const struct client_state *csp, struct http_response *rsp) +struct http_response *finish_http_response(struct client_state *csp, struct http_response *rsp) { char buf[BUFFER_SIZE]; jb_err err; @@ -1558,7 +1588,19 @@ struct http_response *finish_http_response(const struct client_state *csp, struc return rsp; } - /* + /* + * Add "Cross-origin resource sharing" (CORS) headers if enabled + */ + if (NULL != csp->config->cors_allowed_origin) + { + enlist_unique_header(rsp->headers, "Access-Control-Allow-Origin", + strdup_or_die(csp->config->cors_allowed_origin)); + enlist_unique_header(rsp->headers, "Access-Control-Allow-Methods", "GET,POST"); + enlist_unique_header(rsp->headers, "Access-Control-Allow-Headers", "X-Requested-With"); + enlist_unique_header(rsp->headers, "Access-Control-Max-Age", "86400"); + } + + /* * Fill in the HTTP Status, using HTTP/1.1 * unless the client asked for HTTP/1.0. */ @@ -1567,7 +1609,7 @@ struct http_response *finish_http_response(const struct client_state *csp, struc rsp->status ? rsp->status : "200 OK"); err = enlist_first(rsp->headers, buf); - /* + /* * Set the Content-Length */ if (rsp->content_length == 0) @@ -1577,23 +1619,29 @@ struct http_response *finish_http_response(const struct client_state *csp, struc #ifdef FEATURE_COMPRESSION if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE) - && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMRPESSION)) + && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION)) { char *compressed_content; - compressed_content = compress_buffer(rsp->body, &rsp->content_length); + compressed_content = compress_buffer(rsp->body, &rsp->content_length, + csp->config->compression_level); if (NULL != compressed_content) { freez(rsp->body); rsp->body = compressed_content; + err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate"); } - err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate"); } #endif if (!err) { snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length); + /* + * Signal serve() that the client will be able to figure out + * the end of the response without having to close the connection. + */ + csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET; err = enlist(rsp->headers, buf); } @@ -1632,7 +1680,7 @@ struct http_response *finish_http_response(const struct client_state *csp, struc * Last-Modified: set to date/time the page was last changed. * Expires: set to date/time page next needs reloading. * Cache-Control: set to "no-cache" if applicable. - * + * * See http://www.w3.org/Protocols/rfc2068/rfc2068 */ if (rsp->is_static) @@ -1703,12 +1751,13 @@ struct http_response *finish_http_response(const struct client_state *csp, struc if (!err) err = enlist_unique_header(rsp->headers, "Pragma", "no-cache"); } - if (!err && !(csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)) + if (!err && (!(csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE) + || (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))) { err = enlist_unique_header(rsp->headers, "Connection", "close"); } - /* + /* * Write the head */ if (err || (NULL == (rsp->head = list_to_text(rsp->headers)))) @@ -1788,11 +1837,11 @@ void free_http_response(struct http_response *rsp) * following an #include statament * * Returns : JB_ERR_OK on success - * JB_ERR_MEMORY on out-of-memory error. + * JB_ERR_MEMORY on out-of-memory error. * JB_ERR_FILE if the template file cannot be read * *********************************************************************/ -jb_err template_load(const struct client_state *csp, char **template_ptr, +jb_err template_load(const struct client_state *csp, char **template_ptr, const char *templatename, int recursive) { jb_err err; @@ -1874,7 +1923,7 @@ jb_err template_load(const struct client_state *csp, char **template_ptr, } free(full_path); - /* + /* * Read the file, ignoring comments, and honoring #include * statements, unless we're already called recursively. * @@ -1962,7 +2011,7 @@ jb_err template_fill(char **template_ptr, const struct map *exports) file_buffer = *template_ptr; size = strlen(file_buffer) + 1; - /* + /* * Assemble pcrs joblist from exports map */ for (m = exports->first; m != NULL; m = m->next) @@ -1980,7 +2029,7 @@ jb_err template_fill(char **template_ptr, const struct map *exports) else { /* - * Treat the "replace with" text as a literal string - + * Treat the "replace with" text as a literal string - * no quoting needed, no backreferences allowed. * ("Trivial" ['T'] flag). */ @@ -1994,7 +2043,7 @@ jb_err template_fill(char **template_ptr, const struct map *exports) /* Make and run job. */ job = pcrs_compile(buf, m->value, flags, &error); - if (job == NULL) + if (job == NULL) { if (error == PCRS_ERR_NOMEM) { @@ -2021,10 +2070,10 @@ jb_err template_fill(char **template_ptr, const struct map *exports) if (error < 0) { - /* + /* * Substitution failed, keep the original buffer, * log the problem and ignore it. - * + * * The user might see some unresolved @CGI_VARIABLES@, * but returning a special CGI error page seems unreasonable * and could mask more important error messages. @@ -2076,7 +2125,7 @@ jb_err template_fill_for_cgi(const struct client_state *csp, struct http_response *rsp) { jb_err err; - + assert(csp); assert(templatename); assert(exports); @@ -2085,15 +2134,12 @@ jb_err template_fill_for_cgi(const struct client_state *csp, err = template_load(csp, &rsp->body, templatename, 0); if (err == JB_ERR_FILE) { - free_map(exports); - return cgi_error_no_template(csp, rsp, templatename); + err = cgi_error_no_template(csp, rsp, templatename); } - else if (err) + else if (err == JB_ERR_OK) { - free_map(exports); - return err; /* JB_ERR_MEMORY */ + err = template_fill(&rsp->body, exports); } - err = template_fill(&rsp->body, exports); free_map(exports); return err; } @@ -2121,24 +2167,21 @@ struct map *default_exports(const struct client_state *csp, const char *caller) struct map * exports; int local_help_exists = 0; char *ip_address = NULL; + char *port = NULL; char *hostname = NULL; assert(csp); exports = new_map(); - if (exports == NULL) - { - return NULL; - } if (csp->config->hostname) { - get_host_information(csp->cfd, &ip_address, NULL); + get_host_information(csp->cfd, &ip_address, &port, NULL); hostname = strdup(csp->config->hostname); } else { - get_host_information(csp->cfd, &ip_address, &hostname); + get_host_information(csp->cfd, &ip_address, &port, &hostname); } err = map(exports, "version", 1, html_encode(VERSION), 0); @@ -2146,6 +2189,8 @@ struct map *default_exports(const struct client_state *csp, const char *caller) if (!err) err = map(exports, "time", 1, html_encode(buf), 0); if (!err) err = map(exports, "my-ip-address", 1, html_encode(ip_address ? ip_address : "unknown"), 0); freez(ip_address); + if (!err) err = map(exports, "my-port", 1, html_encode(port ? port : "unknown"), 0); + freez(port); if (!err) err = map(exports, "my-hostname", 1, html_encode(hostname ? hostname : "unknown"), 0); freez(hostname); if (!err) err = map(exports, "homepage", 1, html_encode(HOME_PAGE_URL), 0); @@ -2170,10 +2215,7 @@ struct map *default_exports(const struct client_state *csp, const char *caller) if (!err) err = map_block_killer(exports, "can-toggle"); #endif - snprintf(buf, sizeof(buf), "%d", csp->config->hport); - if (!err) err = map(exports, "my-port", 1, buf, 1); - - if(!strcmp(CODE_STATUS, "stable")) + if (!strcmp(CODE_STATUS, "stable")) { if (!err) err = map_block_killer(exports, "unstable"); } @@ -2223,12 +2265,12 @@ struct map *default_exports(const struct client_state *csp, const char *caller) * "if-