pcrs: Request JIT compilation if it's supported
[privoxy.git] / cgi.c
diff --git a/cgi.c b/cgi.c
index 02270ca..7518219 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,24 +1,20 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.134 2011/06/25 12:40:55 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
  *
  * Purpose     :  Declares functions to intercept request, generate
- *                html or gif answers, and to compose HTTP resonses.
+ *                html or gif answers, and to compose HTTP responses.
  *                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-2020
+ *                members of the Privoxy team. https://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
@@ -66,13 +62,16 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.134 2011/06/25 12:40:55 fabiankeil Exp $"
 #if defined(FEATURE_CGI_EDIT_ACTIONS) || defined(FEATURE_TOGGLE)
 #include "cgiedit.h"
 #endif /* defined(FEATURE_CGI_EDIT_ACTIONS) || defined (FEATURE_TOGGLE) */
+#ifdef FEATURE_HTTPS_INSPECTION
+#include "ssl.h"
+#endif
 
 /* loadcfg.h is for global_toggle_state only */
 #include "loadcfg.h"
 /* jcc.h is for mutex semaphore globals only */
 #include "jcc.h"
 
-const char cgi_h_rcs[] = CGI_H_VERSION;
+static char *make_menu(const struct client_state *csp, const char *self);
 
 /*
  * List of CGI functions: name, handler, description
@@ -85,126 +84,139 @@ static const struct cgi_dispatcher cgi_dispatchers[] = {
          "Privoxy main page",
          TRUE },
 #ifdef FEATURE_GRACEFUL_TERMINATION
-   { "die", 
-         cgi_die,  
+   { "die",
+         cgi_die,
          "<b>Shut down</b> - <em class=\"warning\">Do not deploy this build in a production environment, "
         "this is a one click Denial Of Service attack!!!</em>",
-         FALSE }, 
+         FALSE },
 #endif
-   { "show-status", 
-         cgi_show_status,  
+   { "show-status",
+         cgi_show_status,
 #ifdef FEATURE_CGI_EDIT_ACTIONS
         "View &amp; 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 client&#39;s 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,
@@ -237,7 +249,7 @@ const char image_pattern_data[] =
    "\000\000\000\000\111\105\116\104\256\102\140\202";
 
 /*
- * 1x1 transparant PNG.
+ * 1x1 transparent PNG.
  */
 const char image_blank_data[] =
  "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104\122"
@@ -260,7 +272,7 @@ const char image_pattern_data[] =
    "\270\005\000\073";
 
 /*
- * 1x1 transparant GIF.
+ * 1x1 transparent GIF.
  */
 const char image_blank_data[] =
    "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000"
@@ -271,6 +283,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 +299,7 @@ static struct map *parse_cgi_parameters(char *argstring);
 
 
 /*********************************************************************
- * 
+ *
  * Function    :  dispatch_cgi
  *
  * Description :  Checks if a request URL has either the magical
@@ -310,15 +329,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 +361,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.
     */
 
@@ -372,8 +406,13 @@ struct http_response *dispatch_cgi(struct client_state *csp)
 static char *grep_cgi_referrer(const struct client_state *csp)
 {
    struct list_entry *p;
+   struct list_entry *first_header =
+#ifdef FEATURE_HTTPS_INSPECTION
+      client_use_ssl(csp) ? csp->https_headers->first :
+#endif
+      csp->headers->first;
 
-   for (p = csp->headers->first; p != NULL; p = p->next)
+   for (p = first_header; p != NULL; p = p->next)
    {
       if (p->str == NULL) continue;
       if (strncmpic(p->str, "Referer: ", 9) == 0)
@@ -387,7 +426,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 +444,10 @@ static int referrer_is_safe(const struct client_state *csp)
 {
    char *referrer;
    static const char alternative_prefix[] = "http://" CGI_SITE_1_HOST "/";
+#ifdef FEATURE_HTTPS_INSPECTION
+   static const char alt_prefix_https[] = "https://" CGI_SITE_1_HOST "/";
+#endif
+   const char *trusted_cgi_referrer = csp->config->trusted_cgi_referrer;
 
    referrer = grep_cgi_referrer(csp);
 
@@ -414,8 +457,12 @@ static int referrer_is_safe(const struct client_state *csp)
       log_error(LOG_LEVEL_ERROR, "Denying access to %s. No referrer found.",
          csp->http->url);
    }
-   else if ((0 == strncmp(referrer, CGI_PREFIX, sizeof(CGI_PREFIX)-1)
-         || (0 == strncmp(referrer, alternative_prefix, strlen(alternative_prefix)))))
+   else if ((0 == strncmp(referrer, CGI_PREFIX, sizeof(CGI_PREFIX)-1))
+#ifdef FEATURE_HTTPS_INSPECTION
+         || (0 == strncmp(referrer, CGI_PREFIX_HTTPS, sizeof(CGI_PREFIX_HTTPS)-1))
+         || (0 == strncmp(referrer, alt_prefix_https, strlen(alt_prefix_https)))
+#endif
+         || (0 == strncmp(referrer, alternative_prefix, strlen(alternative_prefix))))
    {
       /* Trustworthy referrer */
       log_error(LOG_LEVEL_CGI, "Granting access to %s, referrer %s is trustworthy.",
@@ -423,6 +470,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 +494,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 +532,16 @@ 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()))
+      param_list = new_map();
+      err = map(param_list, "file", 1, url_decode(query_args_start), 0);
+      if (JB_ERR_OK != err)
       {
-         map(param_list, "file", 1, url_decode(query_args_start), 0);
+         free(param_list);
+         free(path_copy);
+         return cgi_error_memory();
       }
    }
    else
@@ -508,7 +571,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;
@@ -572,8 +635,8 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
       d++;
    }
 }
-   
-        
+
+
 /*********************************************************************
  *
  * Function    :  parse_cgi_parameters
@@ -590,16 +653,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:
@@ -609,7 +687,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++)
    {
@@ -618,12 +703,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;
 
 }
@@ -665,22 +753,22 @@ char get_char_param(const struct map *parameters,
  *
  * Function    :  get_string_param
  *
- * Description :  Get a string paramater, to be used as an
- *                ACTION_STRING or ACTION_MULTI paramater.
+ * Description :  Get a string parameter, to be used as an
+ *                ACTION_STRING or ACTION_MULTI parameter.
  *                Validates the input to prevent stupid/malicious
  *                users from corrupting their action file.
  *
  * Parameters  :
  *          1  :  parameters = map of cgi parameters
  *          2  :  param_name = The name of the parameter to read
- *          3  :  pparam = destination for paramater.  Allocated as
+ *          3  :  pparam = destination for parameter.  Allocated as
  *                part of the map "parameters", so don't free it.
  *                Set to NULL if not specified.
  *
- * Returns     :  JB_ERR_OK         on success, or if the paramater
+ * Returns     :  JB_ERR_OK         on success, or if the parameter
  *                                  was not specified.
  *                JB_ERR_MEMORY     on out-of-memory.
- *                JB_ERR_CGI_PARAMS if the paramater is not valid.
+ *                JB_ERR_CGI_PARAMS if the parameter is not valid.
  *
  *********************************************************************/
 jb_err get_string_param(const struct map *parameters,
@@ -719,8 +807,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;
@@ -760,15 +848,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)
@@ -776,36 +863,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:
-       *
-       * <limits.h> defines UINT_MAX
-       *
-       * (UINT_MAX - ch) / 10 is the largest number that
-       *     can be safely multiplied by 10 then have ch added.
-       */
-      if (value > ((UINT_MAX - (unsigned)ch) / 10U))
-      {
-         return JB_ERR_CGI_PARAMS;
-      }
-
-      value = value * 10 + (unsigned)ch;
+      return JB_ERR_CGI_PARAMS;
    }
 
-   /* Success */
-   *pvalue = value;
-
    return JB_ERR_OK;
 
 }
@@ -860,7 +923,7 @@ struct http_response *error_response(struct client_state *csp,
    if (!err) err = map(exports, "host", 1, html_encode(csp->http->host), 0);
    if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
    if (!err) err = map(exports, "path", 1, html_encode_and_free_original(path), 0);
-   if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); 
+   if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
    if (!err)
    {
      err = map(exports, "host-ip", 1, html_encode(csp->http->host_ip_addr_str), 0);
@@ -930,6 +993,12 @@ struct http_response *error_response(struct client_state *csp,
          case SOCKS_5:
             socks_type = "socks5-";
             break;
+         case SOCKS_5T:
+            socks_type = "socks5t-";
+            break;
+         case FORWARD_WEBSERVER:
+            socks_type = "webserver-";
+            break;
          default:
             log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
       }
@@ -1009,6 +1078,8 @@ jb_err cgi_error_disabled(const struct client_state *csp,
    assert(csp);
    assert(rsp);
 
+   rsp->status = strdup_or_die("403 Request not trusted or feature disabled");
+
    if (NULL == (exports = default_exports(csp, "cgi-error-disabled")))
    {
       return JB_ERR_MEMORY;
@@ -1091,7 +1162,7 @@ struct http_response *cgi_error_memory(void)
  *
  * Description :  Almost-CGI function that is called if a template
  *                cannot be loaded.  Note this is not a true CGI,
- *                it takes a template name rather than a map of 
+ *                it takes a template name rather than a map of
  *                parameters.
  *
  * Parameters  :
@@ -1101,7 +1172,7 @@ struct http_response *cgi_error_memory(void)
  *                                be loaded.
  *
  * 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_no_template(const struct client_state *csp,
@@ -1148,11 +1219,7 @@ jb_err cgi_error_no_template(const struct client_state *csp,
    rsp->head_length = 0;
    rsp->is_static = 0;
 
-   rsp->body = malloc(body_size);
-   if (rsp->body == NULL)
-   {
-      return JB_ERR_MEMORY;
-   }
+   rsp->body = malloc_or_die(body_size);
    strlcpy(rsp->body, body_prefix, body_size);
    strlcat(rsp->body, template_name, body_size);
    strlcat(rsp->body, body_suffix, body_size);
@@ -1176,7 +1243,7 @@ jb_err cgi_error_no_template(const struct client_state *csp,
  *                In this context, "unexpected" means "anything other
  *                than JB_ERR_MEMORY or JB_ERR_CGI_PARAMS" - CGIs are
  *                expected to handle all other errors internally,
- *                since they can give more relavent error messages
+ *                since they can give more relevant error messages
  *                that way.
  *
  *                Note this is not a true CGI, it takes an error
@@ -1188,7 +1255,7 @@ jb_err cgi_error_no_template(const struct client_state *csp,
  *          3  :  error_to_report = Error code to report.
  *
  * 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_unknown(const struct client_state *csp,
@@ -1228,11 +1295,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);
 
@@ -1252,7 +1315,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
@@ -1260,7 +1323,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,
@@ -1282,7 +1345,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
@@ -1294,7 +1357,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)
@@ -1329,8 +1392,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.
  *
@@ -1375,7 +1438,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.
@@ -1388,12 +1451,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)
@@ -1418,17 +1475,7 @@ void get_http_time(int time_offset, char *buf, size_t buffer_size)
    t = gmtime(&current_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);
 
 }
 
@@ -1469,13 +1516,15 @@ static void get_locale_time(char *buf, size_t buffer_size)
 #elif defined(MUTEX_LOCKS_AVAILABLE)
    privoxy_mutex_lock(&localtime_mutex);
    timeptr = localtime(&current_time);
-   privoxy_mutex_unlock(&localtime_mutex);
 #else
    timeptr = localtime(&current_time);
 #endif
 
    strftime(buf, buffer_size, "%a %b %d %X %Z %Y", timeptr);
 
+#if !defined(HAVE_LOCALTIME_R) && defined(MUTEX_LOCKS_AVAILABLE)
+   privoxy_mutex_unlock(&localtime_mutex);
+#endif
 }
 
 
@@ -1488,42 +1537,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: %lu, 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 %lu to %lu bytes. Compression level: %d",
+      *buffer_length, new_length, compression_level);
 
-   *buffer_length = new_length;
+   *buffer_length = (size_t)new_length;
 
    return compressed_buffer;
 
@@ -1547,7 +1595,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;
@@ -1560,16 +1608,28 @@ 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",
+         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.
     */
    snprintf(buf, sizeof(buf), "%s %s",
-      strcmpic(csp->http->ver, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0",
+      strcmpic(csp->http->version, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0",
       rsp->status ? rsp->status : "200 OK");
    err = enlist_first(rsp->headers, buf);
 
-   /* 
+   /*
     * Set the Content-Length
     */
    if (rsp->content_length == 0)
@@ -1579,11 +1639,12 @@ 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);
@@ -1596,6 +1657,11 @@ struct http_response *finish_http_response(const struct client_state *csp, struc
    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);
    }
 
@@ -1634,7 +1700,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)
@@ -1705,12 +1771,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))))
@@ -1790,11 +1857,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;
@@ -1876,7 +1943,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.
     *
@@ -1964,7 +2031,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)
@@ -1982,7 +2049,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).
           */
@@ -1996,7 +2063,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)
          {
@@ -2023,10 +2090,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.
@@ -2078,7 +2145,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);
@@ -2087,15 +2154,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;
 }
@@ -2123,24 +2187,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);
@@ -2148,11 +2209,20 @@ 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);
-   if (!err) err = map(exports, "default-cgi",   1, html_encode(CGI_PREFIX), 0);
-   if (!err) err = map(exports, "menu",          1, make_menu(caller, csp->config->feature_flags), 0);
+   if (!err)
+   {
+      err = map(exports, "default-cgi",   1, html_encode(
+#ifdef FEATURE_HTTPS_INSPECTION
+        client_use_ssl(csp) ? CGI_PREFIX_HTTPS :
+#endif
+        CGI_PREFIX), 0);
+   }
+   if (!err) err = map(exports, "menu",          1, make_menu(csp, caller), 0);
    if (!err) err = map(exports, "code-status",   1, CODE_STATUS, 1);
    if (!strncmpic(csp->config->usermanual, "file://", 7) ||
        !strncmpic(csp->config->usermanual, "http", 4))
@@ -2163,7 +2233,14 @@ struct map *default_exports(const struct client_state *csp, const char *caller)
    else
    {
       /* Manual is delivered by Privoxy. */
-      if (!err) err = map(exports, "user-manual", 1, html_encode(CGI_PREFIX"user-manual/"), 0);
+      if (!err)
+      {
+         err = map(exports, "user-manual", 1, html_encode(
+#ifdef FEATURE_HTTPS_INSPECTION
+           client_use_ssl(csp) ? CGI_PREFIX_HTTPS"user-manual/" :
+#endif
+           CGI_PREFIX"user-manual/"), 0);
+      }
    }
    if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1);
 #ifdef FEATURE_TOGGLE
@@ -2172,10 +2249,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");
    }
@@ -2225,12 +2299,12 @@ struct map *default_exports(const struct client_state *csp, const char *caller)
  *                "if-<name>-start.*if-<name>-end" to the given
  *                export list.
  *
- * Parameters  :  
+ * Parameters  :
  *          1  :  exports = map to extend
  *          2  :  name = name of conditional block
  *
  * Returns     :  JB_ERR_OK on success
- *                JB_ERR_MEMORY on out-of-memory error.  
+ *                JB_ERR_MEMORY on out-of-memory error.
  *
  *********************************************************************/
 jb_err map_block_killer(struct map *exports, const char *name)
@@ -2254,12 +2328,12 @@ jb_err map_block_killer(struct map *exports, const char *name)
  *                by map-block-killer, to save a few bytes.
  *                i.e. removes "@if-<name>-start@" and "@if-<name>-end@"
  *
- * Parameters  :  
+ * Parameters  :
  *          1  :  exports = map to extend
  *          2  :  name = name of conditional block
  *
  * Returns     :  JB_ERR_OK on success
- *                JB_ERR_MEMORY on out-of-memory error.  
+ *                JB_ERR_MEMORY on out-of-memory error.
  *
  *********************************************************************/
 jb_err map_block_keep(struct map *exports, const char *name)
@@ -2300,13 +2374,13 @@ jb_err map_block_keep(struct map *exports, const char *name)
  *                The control structure and one of the alternatives
  *                will be hidden.
  *
- * Parameters  :  
+ * Parameters  :
  *          1  :  exports = map to extend
  *          2  :  name = name of conditional block
  *          3  :  choose_first = nonzero for first, zero for second.
  *
  * Returns     :  JB_ERR_OK on success
- *                JB_ERR_MEMORY on out-of-memory error.  
+ *                JB_ERR_MEMORY on out-of-memory error.
  *
  *********************************************************************/
 jb_err map_conditional(struct map *exports, const char *name, int choose_first)
@@ -2338,20 +2412,19 @@ jb_err map_conditional(struct map *exports, const char *name, int choose_first)
  *
  * Function    :  make_menu
  *
- * Description :  Returns an HTML-formatted menu of the available 
+ * Description :  Returns an HTML-formatted menu of the available
  *                unhidden CGIs, excluding the one given in <self>
  *                and the toggle CGI if toggling is disabled.
  *
  * Parameters  :
- *          1  :  self = name of CGI to leave out, can be NULL for
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  self = name of CGI to leave out, can be NULL for
  *                complete listing.
- *          2  :  feature_flags = feature bitmap from csp->config
- *                
  *
  * Returns     :  menu string, or NULL on out-of-memory error.
  *
  *********************************************************************/
-char *make_menu(const char *self, const unsigned feature_flags)
+char *make_menu(const struct client_state *csp, const char *self)
 {
    const struct cgi_dispatcher *d;
    char *result = strdup("");
@@ -2366,7 +2439,7 @@ char *make_menu(const char *self, const unsigned feature_flags)
    {
 
 #ifdef FEATURE_TOGGLE
-      if (!(feature_flags & RUNTIME_FEATURE_CGI_TOGGLE) && !strcmp(d->name, "toggle"))
+      if (!(csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE) && !strcmp(d->name, "toggle"))
       {
          /*
           * Suppress the toggle link if remote toggling is disabled.
@@ -2384,10 +2457,14 @@ char *make_menu(const char *self, const unsigned feature_flags)
           * the "blocked" template's JavaScript.
           */
          string_append(&result, "<li><a href=\"");
-         html_encoded_prefix = html_encode(CGI_PREFIX);
+         html_encoded_prefix = html_encode(
+#ifdef FEATURE_HTTPS_INSPECTION
+            client_use_ssl(csp) ? CGI_PREFIX_HTTPS :
+#endif
+            CGI_PREFIX);
          if (html_encoded_prefix == NULL)
          {
-            return NULL;  
+            return NULL;
          }
          else
          {