Fix a memory leak if the socks5 server is unreachable.
[privoxy.git] / cgi.c
diff --git a/cgi.c b/cgi.c
index 93af14b..d4a1f22 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.125 2009/06/14 14:37:08 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.139 2011/07/08 13:27:56 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -49,6 +49,10 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.125 2009/06/14 14:37:08 fabiankeil Exp $"
 #include <limits.h>
 #include <assert.h>
 
+#ifdef FEATURE_COMPRESSION
+#include <zlib.h>
+#endif
+
 #include "project.h"
 #include "cgi.h"
 #include "list.h"
@@ -267,6 +271,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];
 
@@ -547,13 +558,15 @@ 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)
          {
             /* It worked */
-            rsp->reason = RSP_REASON_CGI_CALL;
+            rsp->crunch_reason = CGI_CALL;
             return finish_http_response(csp, rsp);
          }
          else
@@ -876,7 +889,7 @@ struct http_response *error_response(struct client_state *csp,
    if (!strcmp(templatename, "no-such-domain"))
    {
       rsp->status = strdup("404 No such domain");
-      rsp->reason = RSP_REASON_NO_SUCH_DOMAIN;
+      rsp->crunch_reason = NO_SUCH_DOMAIN;
    }
    else if (!strcmp(templatename, "forwarding-failed"))
    {
@@ -938,22 +951,22 @@ struct http_response *error_response(struct client_state *csp,
       }
 
       rsp->status = strdup("503 Forwarding failure");
-      rsp->reason = RSP_REASON_FORWARDING_FAILED;
+      rsp->crunch_reason = FORWARDING_FAILED;
    }
    else if (!strcmp(templatename, "connect-failed"))
    {
       rsp->status = strdup("503 Connect failed");
-      rsp->reason = RSP_REASON_CONNECT_FAILED;
+      rsp->crunch_reason = CONNECT_FAILED;
    }
    else if (!strcmp(templatename, "connection-timeout"))
    {
       rsp->status = strdup("504 Connection timeout");
-      rsp->reason = RSP_REASON_CONNECTION_TIMEOUT;
+      rsp->crunch_reason = CONNECTION_TIMEOUT;
    }
    else if (!strcmp(templatename, "no-server-data"))
    {
       rsp->status = strdup("502 No data received from server or forwarder");
-      rsp->reason = RSP_REASON_NO_SERVER_DATA;
+      rsp->crunch_reason = NO_SERVER_DATA;
    }
 
    if (rsp->status == NULL)
@@ -1037,23 +1050,23 @@ void cgi_init_error_messages(void)
       "Content-Type: text/html\r\n"
       "\r\n";
    cgi_error_memory_response->body =
-      "<html>\r\n"
-      "<head>\r\n"
-      " <title>500 Internal Privoxy Error</title>\r\n"
+      "<html>\n"
+      "<head>\n"
+      " <title>500 Internal Privoxy Error</title>\n"
       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
-      "</head>\r\n"
-      "<body>\r\n"
-      "<h1>500 Internal Privoxy Error</h1>\r\n"
-      "<p>Privoxy <b>ran out of memory</b> while processing your request.</p>\r\n"
-      "<p>Please contact your proxy administrator, or try again later</p>\r\n"
-      "</body>\r\n"
-      "</html>\r\n";
+      "</head>\n"
+      "<body>\n"
+      "<h1>500 Internal Privoxy Error</h1>\n"
+      "<p>Privoxy <b>ran out of memory</b> while processing your request.</p>\n"
+      "<p>Please contact your proxy administrator, or try again later</p>\n"
+      "</body>\n"
+      "</html>\n";
 
    cgi_error_memory_response->head_length =
       strlen(cgi_error_memory_response->head);
    cgi_error_memory_response->content_length =
       strlen(cgi_error_memory_response->body);
-   cgi_error_memory_response->reason = RSP_REASON_OUT_OF_MEMORY;
+   cgi_error_memory_response->crunch_reason = OUT_OF_MEMORY;
 }
 
 
@@ -1105,18 +1118,18 @@ jb_err cgi_error_no_template(const struct client_state *csp,
    static const char status[] =
       "500 Internal Privoxy Error";
    static const char body_prefix[] =
-      "<html>\r\n"
-      "<head>\r\n"
-      " <title>500 Internal Privoxy Error</title>\r\n"
+      "<html>\n"
+      "<head>\n"
+      " <title>500 Internal Privoxy Error</title>\n"
       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
-      "</head>\r\n"
-      "<body>\r\n"
-      "<h1>500 Internal Privoxy Error</h1>\r\n"
-      "<p>Privoxy encountered an error while processing your request:</p>\r\n"
+      "</head>\n"
+      "<body>\n"
+      "<h1>500 Internal Privoxy Error</h1>\n"
+      "<p>Privoxy encountered an error while processing your request:</p>\n"
       "<p><b>Could not load template file <code>";
    static const char body_suffix[] =
-      "</code> or one of its included components.</b></p>\r\n"
-      "<p>Please contact your proxy administrator.</p>\r\n"
+      "</code> or one of its included components.</b></p>\n"
+      "<p>Please contact your proxy administrator.</p>\n"
       "<p>If you are the proxy administrator, please put the required file(s)"
       "in the <code><i>(confdir)</i>/templates</code> directory.  The "
       "location of the <code><i>(confdir)</i></code> directory "
@@ -1125,9 +1138,9 @@ jb_err cgi_error_no_template(const struct client_state *csp,
 #ifndef _WIN32
       ", or <code>/etc/privoxy/</code>"
 #endif /* ndef _WIN32 */
-      ").</p>\r\n"
-      "</body>\r\n"
-      "</html>\r\n";
+      ").</p>\n"
+      "</body>\n"
+      "</html>\n";
    const size_t body_size = strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1;
 
    assert(csp);
@@ -1192,28 +1205,24 @@ jb_err cgi_error_unknown(const struct client_state *csp,
    static const char status[] =
       "500 Internal Privoxy Error";
    static const char body_prefix[] =
-      "<html>\r\n"
-      "<head>\r\n"
-      " <title>500 Internal Privoxy Error</title>\r\n"
+      "<html>\n"
+      "<head>\n"
+      " <title>500 Internal Privoxy Error</title>\n"
       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
-      "</head>\r\n"
-      "<body>\r\n"
-      "<h1>500 Internal Privoxy Error</h1>\r\n"
-      "<p>Privoxy encountered an error while processing your request:</p>\r\n"
+      "</head>\n"
+      "<body>\n"
+      "<h1>500 Internal Privoxy Error</h1>\n"
+      "<p>Privoxy encountered an error while processing your request:</p>\n"
       "<p><b>Unexpected internal error: ";
    static const char body_suffix[] =
-      "</b></p>\r\n"
+      "</b></p>\n"
       "<p>Please "
       "<a href=\"http://sourceforge.net/tracker/?group_id=11118&amp;atid=111118\">"
-      "file a bug report</a>.</p>\r\n"
-      "</body>\r\n"
-      "</html>\r\n";
-   char errnumbuf[30];
-   /*
-    * Due to sizeof(errnumbuf), body_size will be slightly
-    * bigger than necessary but it doesn't really matter.
-    */
-   const size_t body_size = strlen(body_prefix) + sizeof(errnumbuf) + strlen(body_suffix) + 1;
+      "file a bug report</a>.</p>\n"
+      "</body>\n"
+      "</html>\n";
+   /* Includes room for larger error numbers in the future. */
+   const size_t body_size = sizeof(body_prefix) + sizeof(body_suffix) + 5;
    assert(csp);
    assert(rsp);
 
@@ -1224,18 +1233,15 @@ jb_err cgi_error_unknown(const struct client_state *csp,
    rsp->content_length = 0;
    rsp->head_length = 0;
    rsp->is_static = 0;
-   rsp->reason = RSP_REASON_INTERNAL_ERROR;
-
-   snprintf(errnumbuf, sizeof(errnumbuf), "%d", error_to_report);
+   rsp->crunch_reason = INTERNAL_ERROR;
 
    rsp->body = malloc(body_size);
    if (rsp->body == NULL)
    {
       return JB_ERR_MEMORY;
    }
-   strlcpy(rsp->body, body_prefix, body_size);
-   strlcat(rsp->body, errnumbuf,   body_size);
-   strlcat(rsp->body, body_suffix, body_size);
+
+   snprintf(rsp->body, body_size, "%s%d%s", body_prefix, error_to_report, body_suffix);
 
    rsp->status = strdup(status);
    if (rsp->status == NULL)
@@ -1479,6 +1485,71 @@ static void get_locale_time(char *buf, size_t buffer_size)
 
 }
 
+
+#ifdef FEATURE_COMPRESSION
+/*********************************************************************
+ *
+ * Function    :  compress_buffer
+ *
+ * Description :  Compresses the content of a buffer with zlib's deflate
+ *                Allocates a new buffer for the result, free'ing it is
+ *                up to the caller.
+ *
+ * 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, int compression_level)
+{
+   char *compressed_buffer;
+   size_t new_length;
+   assert(-1 <= compression_level && compression_level <= 9);
+
+   /*
+    * If the compression level is 0 or if the entropy
+    * is high, the "compressing" data will take more
+    * room then the uncompressed data due to the zlib
+    * overhead.
+    *
+    * XXX: The overhead isn't constant and 30 bytes
+    *      may not be enough for everybody
+    */
+   new_length = *buffer_length + 30;
+
+   compressed_buffer = malloc(new_length);
+   if (NULL == compressed_buffer)
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Out of memory allocation compression buffer.");
+   }
+
+   if (Z_OK != compress2((Bytef *)compressed_buffer, &new_length,
+         (Bytef *)buffer, *buffer_length, compression_level))
+   {
+      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. Compression level: %d",
+      *buffer_length, new_length, compression_level);
+
+   *buffer_length = new_length;
+
+   return compressed_buffer;
+
+}
+#endif
+
+
 /*********************************************************************
  *
  * Function    :  finish_http_response
@@ -1524,6 +1595,24 @@ struct http_response *finish_http_response(const struct client_state *csp, struc
    {
       rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
    }
+
+#ifdef FEATURE_COMPRESSION
+   if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+      && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+   {
+      char *compressed_content;
+
+      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");
+      }
+   }
+#endif
+
    if (!err)
    {
       snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length);
@@ -1636,13 +1725,10 @@ struct http_response *finish_http_response(const struct client_state *csp, struc
       if (!err) err = enlist_unique_header(rsp->headers, "Pragma", "no-cache");
    }
 
-   /*
-    * Quoting RFC 2616:
-    *
-    * HTTP/1.1 applications that do not support persistent connections MUST
-    * include the "close" connection option in every message.
-    */
-   if (!err) err = enlist_unique_header(rsp->headers, "Connection", "close");
+   if (!err && !(csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE))
+   {
+      err = enlist_unique_header(rsp->headers, "Connection", "close");
+   }
 
    /* 
     * Write the head
@@ -1865,7 +1951,7 @@ jb_err template_load(const struct client_state *csp, char **template_ptr,
  *                HTML template by replacing @name@ with value using
  *                pcrs, for each item in the output map.
  *
- *                Note that a leading '$' charachter in the export map's
+ *                Note that a leading '$' character in the export map's
  *                values will be stripped and toggle on backreference
  *                interpretation.
  *