- Save the reason for generating http_responses.
[privoxy.git] / cgi.c
diff --git a/cgi.c b/cgi.c
index f2d2429..7816c37 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,4 +1,4 @@
-const char cgi_rcs[] = "$Id: cgi.c,v 1.88 2007/01/23 13:14:32 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.92 2007/01/28 13:41:17 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
@@ -38,6 +38,20 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.88 2007/01/23 13:14:32 fabiankeil Exp $";
  *
  * Revisions   :
  *    $Log: cgi.c,v $
+ *    Revision 1.92  2007/01/28 13:41:17  fabiankeil
+ *    - Add HEAD support to finish_http_response.
+ *    - Add error favicon to internal HTML error messages.
+ *
+ *    Revision 1.91  2007/01/27 13:09:16  fabiankeil
+ *    Add new config option "templdir" to
+ *    change the templates directory.
+ *
+ *    Revision 1.90  2007/01/25 13:47:26  fabiankeil
+ *    Added "forwarding-failed" template support for error_response().
+ *
+ *    Revision 1.89  2007/01/23 15:51:16  fabiankeil
+ *    Add favicon delivery functions.
+ *
  *    Revision 1.88  2007/01/23 13:14:32  fabiankeil
  *    - Map variables that aren't guaranteed to be
  *      pure ASCII html_encoded.
@@ -562,6 +576,7 @@ const char cgi_rcs[] = "$Id: cgi.c,v 1.88 2007/01/23 13:14:32 fabiankeil Exp $";
 #include "encode.h"
 #include "ssplit.h"
 #include "errlog.h"
+#include "filters.h"
 #include "miscutil.h"
 #include "cgisimple.h"
 #ifdef FEATURE_CGI_EDIT_ACTIONS
@@ -1006,10 +1021,6 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
       return cgi_error_memory();
    }
 
-   log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
-   log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
-                            csp->ip_addr_str, csp->http->cmd); 
-
    /* 
     * Find and start the right CGI function
     */
@@ -1059,7 +1070,8 @@ static struct http_response *dispatch_known_cgi(struct client_state * csp,
          if (!err)
          {
             /* It worked */
-            return finish_http_response(rsp);
+            rsp->reason = RSP_REASON_CGI_CALL;
+            return finish_http_response(csp, rsp);
          }
          else
          {
@@ -1346,11 +1358,13 @@ struct http_response *error_response(struct client_state *csp,
       return cgi_error_memory();
    }
 
+#ifdef FEATURE_FORCE_LOAD
    if (csp->flags & CSP_FLAG_FORCED)
    {
       path = strdup(FORCE_PREFIX);
    }
    else
+#endif /* def FEATURE_FORCE_LOAD */
    {
       path = strdup("");
    }
@@ -1388,6 +1402,48 @@ struct http_response *error_response(struct client_state *csp,
          free_http_response(rsp);
          return cgi_error_memory();
       }
+      rsp->reason = RSP_REASON_NO_SUCH_DOMAIN;
+   }
+   else if (!strcmp(templatename, "forwarding-failed"))
+   {
+      const struct forward_spec * fwd = forward_url(csp->http, csp);
+      if (fwd == NULL)
+      {
+         log_error(LOG_LEVEL_FATAL, "gateway spec is NULL. This shouldn't happen!");
+         /* Never get here - LOG_LEVEL_FATAL causes program exit */
+      }
+
+      /*
+       * XXX: While the template is called forwarding-failed,
+       * it currently only handles socks forwarding failures.
+       */
+      assert(fwd->type != SOCKS_NONE);
+
+      /*
+       * Map failure reason, forwarding type and forwarder.
+       */
+      if (NULL == csp->error_message)
+      {
+         /*
+          * Either we forgot to record the failure reason,
+          * or the memory allocation failed.
+          */
+         log_error(LOG_LEVEL_ERROR, "Socks failure reason missing.");
+         csp->error_message = strdup("Failure reason missing. Check the log file for details.");
+      }
+      if (!err) err = map(exports, "gateway", 1, fwd->gateway_host, 1);
+      if (!err) map(exports, "forwarding-type", 1, (fwd->type == SOCKS_4) ?
+                   "socks4-" : "socks4a-", 1);
+      if (!err) err = map(exports, "error-message", 1, html_encode(csp->error_message), 0);
+
+      if (!err) rsp->status = strdup("503 Forwarding failure");
+      if ((rsp->status == NULL) || (NULL == csp->error_message) || err)
+      {
+         free_map(exports);
+         free_http_response(rsp);
+         return cgi_error_memory();
+      }
+      rsp->reason = RSP_REASON_FORWARDING_FAILED;
    }
    else if (!strcmp(templatename, "connect-failed"))
    {
@@ -1398,6 +1454,7 @@ struct http_response *error_response(struct client_state *csp,
          free_http_response(rsp);
          return cgi_error_memory();
       }
+      rsp->reason = RSP_REASON_CONNECT_FAILED;
    }
 
    err = template_fill_for_cgi(csp, templatename, exports, rsp);
@@ -1407,7 +1464,7 @@ struct http_response *error_response(struct client_state *csp,
       return cgi_error_memory();
    }
 
-   return finish_http_response(rsp);
+   return finish_http_response(csp, rsp);
 }
 
 
@@ -1475,7 +1532,10 @@ void cgi_init_error_messages(void)
       "\r\n";
    cgi_error_memory_response->body =
       "<html>\r\n"
-      "<head><title>500 Internal Privoxy Error</title></head>\r\n"
+      "<head>\r\n"
+      " <title>500 Internal Privoxy Error</title>\r\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"
@@ -1487,6 +1547,7 @@ void cgi_init_error_messages(void)
       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;
 }
 
 
@@ -1539,7 +1600,10 @@ jb_err cgi_error_no_template(struct client_state *csp,
       "500 Internal Privoxy Error";
    static const char body_prefix[] =
       "<html>\r\n"
-      "<head><title>500 Internal Privoxy Error</title></head>\r\n"
+      "<head>\r\n"
+      " <title>500 Internal Privoxy Error</title>\r\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"
@@ -1622,7 +1686,10 @@ jb_err cgi_error_unknown(struct client_state *csp,
       "500 Internal Privoxy Error";
    static const char body_prefix[] =
       "<html>\r\n"
-      "<head><title>500 Internal Privoxy Error</title></head>\r\n"
+      "<head>\r\n"
+      " <title>500 Internal Privoxy Error</title>\r\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"
@@ -1630,7 +1697,7 @@ jb_err cgi_error_unknown(struct client_state *csp,
    static const char body_suffix[] =
       "</b></p>\r\n"
       "<p>Please "
-      "<a href=\"http://sourceforge.net/tracker/?group_id=11118&atid=111118\">"
+      "<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";
@@ -1645,6 +1712,7 @@ jb_err cgi_error_unknown(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);
 
@@ -1865,6 +1933,8 @@ void get_http_time(int time_offset, char *buf)
  *
  * Description :  Fill in the missing headers in an http response,
  *                and flatten the headers to an http head.
+ *                For HEAD requests the body is freed once
+ *                the Content-Length header is set.
  *
  * Parameters  :
  *          1  :  rsp = pointer to http_response to be processed
@@ -1873,7 +1943,7 @@ void get_http_time(int time_offset, char *buf)
  *                On error, free()s rsp and returns cgi_error_memory()
  *
  *********************************************************************/
-struct http_response *finish_http_response(struct http_response *rsp)
+struct http_response *finish_http_response(const struct client_state *csp, struct http_response *rsp)
 {
    char buf[BUFFER_SIZE];
    jb_err err;
@@ -1905,6 +1975,24 @@ struct http_response *finish_http_response(struct http_response *rsp)
       err = enlist(rsp->headers, buf);
    }
 
+   if (0 == strcmpic(csp->http->gpc, "head"))
+   {
+      /*
+       * The client only asked for the head. Dispose
+       * the body and log an offensive message.
+       *
+       * While it may seem to be a bit inefficient to
+       * prepare the body if it isn't needed, it's the
+       * only way to get the Content-Length right for
+       * dynamic pages. We could have disposed the body
+       * earlier, but not without duplicating the
+       * Content-Length setting code above.
+       */
+      log_error(LOG_LEVEL_CGI, "Preparing to give head to %s.", csp->ip_addr_str);
+      freez(rsp->body);
+      rsp->content_length = 0;
+   }
+
    if (strncmpic(rsp->status, "302", 3))
    {
      /*
@@ -1966,7 +2054,7 @@ struct http_response *finish_http_response(struct http_response *rsp)
        * is older than Privoxy's error message, the server would send status code
        * 304 and the browser would display the outdated error message again and again.
        *
-       * For documents delivered with status code 404 or 503 we set "Last-Modified"
+       * For documents delivered with status code 403, 404 and 503 we set "Last-Modified"
        * to Tim Berners-Lee's birthday, which predates the age of any page on the web
        * and can be safely used to "revalidate" without getting a status code 304.
        *
@@ -1977,7 +2065,9 @@ struct http_response *finish_http_response(struct http_response *rsp)
 
       get_http_time(0, buf);
       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
-      if (!strncmpic(rsp->status, "404", 3) || !strncmpic(rsp->status, "503", 3))
+      if (!strncmpic(rsp->status, "403", 3)
+       || !strncmpic(rsp->status, "404", 3)
+       || !strncmpic(rsp->status, "503", 3))
       {
          if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Wed, 08 Jun 1955 12:00:00 GMT");
       }
@@ -2065,9 +2155,8 @@ void free_http_response(struct http_response *rsp)
  * Function    :  template_load
  *
  * Description :  CGI support function that loads a given HTML
- *                template from the confdir, ignoring comment
- *                lines and following #include statements up to
- *                a depth of 1.
+ *                template, ignoring comment lines and following
+ *                #include statements up to a depth of 1.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -2114,11 +2203,23 @@ jb_err template_load(struct client_state *csp, char **template_ptr,
       }
    }
 
-   /* Generate full path */
+   /*
+    * Generate full path using either templdir
+    * or confdir/templates as base directory.
+    */
+   if (NULL != csp->config->templdir)
+   {
+      templates_dir_path = strdup(csp->config->templdir);
+   }
+   else
+   {
+      templates_dir_path = make_path(csp->config->confdir, "templates");
+   }
 
-   templates_dir_path = make_path(csp->config->confdir, "templates");
    if (templates_dir_path == NULL)
    {
+      log_error(LOG_LEVEL_ERROR, "Out of memory while generating template path for %s.",
+         templatename);
       return JB_ERR_MEMORY;
    }
 
@@ -2126,6 +2227,8 @@ jb_err template_load(struct client_state *csp, char **template_ptr,
    free(templates_dir_path);
    if (full_path == NULL)
    {
+      log_error(LOG_LEVEL_ERROR, "Out of memory while generating full template path for %s.",
+         templatename);
       return JB_ERR_MEMORY;
    }
 
@@ -2134,6 +2237,7 @@ jb_err template_load(struct client_state *csp, char **template_ptr,
    file_buffer = strdup("");
    if (file_buffer == NULL)
    {
+      log_error(LOG_LEVEL_ERROR, "Not enough free memory to buffer %s.", full_path);
       free(full_path);
       return JB_ERR_MEMORY;
    }
@@ -2637,6 +2741,10 @@ char *make_menu(const char *self, const unsigned feature_flags)
       {
          char *html_encoded_prefix;
 
+         /*
+          * Line breaks would be great, but break
+          * the "blocked" template's JavaScript.
+          */
          string_append(&result, "<li><a href=\"");
          html_encoded_prefix = html_encode(CGI_PREFIX);
          if (html_encoded_prefix == NULL)
@@ -2651,7 +2759,7 @@ char *make_menu(const char *self, const unsigned feature_flags)
          string_append(&result, d->name);
          string_append(&result, "\">");
          string_append(&result, d->description);
-         string_append(&result, "</a></li>\n");
+         string_append(&result, "</a></li>");
       }
    }