parse_toggle_state() only returns 0 or 1, so check for 1 directly.
[privoxy.git] / cgisimple.c
index b416eb0..3ab3428 100644 (file)
@@ -1,4 +1,4 @@
-const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.93 2009/05/16 13:27:20 fabiankeil Exp $";
+const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.109 2011/04/19 13:00:47 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
@@ -9,7 +9,7 @@ const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.93 2009/05/16 13:27:20 fabian
  *                Functions declared include:
  * 
  *
- * Copyright   :  Written by and Copyright (C) 2001-2008 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2011 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -181,6 +181,20 @@ jb_err cgi_die (struct client_state *csp,
                 struct http_response *rsp,
                 const struct map *parameters)
 {
+   static const char status[] = "200 OK Privoxy shutdown request received";
+   static const char body[] =
+      "<html>\n"
+      "<head>\n"
+      " <title>Privoxy shutdown request received</title>\n"
+      " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">\n"
+      " <link rel=\"stylesheet\" type=\"text/css\" href=\"http://config.privoxy.org/send-stylesheet\">\n"
+      "</head>\n"
+      "<body>\n"
+      "<h1>Privoxy shutdown request received</h1>\n"
+      "<p>Privoxy is going to shut down after the next request.</p>\n"
+      "</body>\n"
+      "</html>\n";
+
    assert(csp);
    assert(rsp);
    assert(parameters);
@@ -188,12 +202,21 @@ jb_err cgi_die (struct client_state *csp,
    /* quit */
    g_terminate = 1;
 
-   /*
-    * I don't really care what gets sent back to the browser.
-    * Take the easy option - "out of memory" page.
-    */
+   csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+
+   rsp->content_length = 0;
+   rsp->head_length = 0;
+   rsp->is_static = 0;
+
+   rsp->body = strdup(body);
+   rsp->status = strdup(status);
+
+   if ((rsp->body == NULL) || (rsp->status == NULL))
+   {
+      return JB_ERR_MEMORY;
+   }
 
-   return JB_ERR_MEMORY;
+   return JB_ERR_OK;
 }
 #endif /* def FEATURE_GRACEFUL_TERMINATION */
 
@@ -658,6 +681,48 @@ jb_err cgi_send_url_info_osd(struct client_state *csp,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  get_content_type
+ *
+ * Description :  Use the file extension to guess the content type
+ *                header we should use to serve the file.
+ *
+ * Parameters  :
+ *          1  :  filename = Name of the file whose content type
+ *                           we care about
+ *
+ * Returns     :  The guessed content type.
+ *
+ *********************************************************************/
+static const char *get_content_type(const char *filename)
+{
+   int i;
+   struct content_type
+   {
+      const char *extension;
+      const char *content_type;
+   };
+   static const struct content_type content_types[] =
+   {
+      {".css",  "text/css"},
+      {".jpg",  "image/jpeg"},
+      {".jpeg", "image/jpeg"},
+      {".png",  "image/png"},
+   };
+
+   for (i = 0; i < SZ(content_types); i++)
+   {
+      if (strstr(filename, content_types[i].extension))
+      {
+         return content_types[i].content_type;
+      }
+   }
+
+   /* No match by extension, default to html */
+   return "text/html";
+}
+
 /*********************************************************************
  *
  * Function    :  cgi_send_user_manual
@@ -681,15 +746,22 @@ jb_err cgi_send_user_manual(struct client_state *csp,
                             struct http_response *rsp,
                             const struct map *parameters)
 {
-   const char * filename;
+   const char *filename;
    char *full_path;
    jb_err err = JB_ERR_OK;
-   size_t length;
+   const char *content_type;
 
    assert(csp);
    assert(rsp);
    assert(parameters);
 
+   if (0 == strncmpic(csp->config->usermanual, "http://", 7))
+   {
+      log_error(LOG_LEVEL_CGI, "Request for local user-manual "
+         "received while user-manual delivery is disabled.");
+      return cgi_error_404(csp, rsp, parameters);
+   }
+
    if (!parameters->first)
    {
       /* requested http://p.p/user-manual (without trailing slash) */
@@ -697,17 +769,24 @@ jb_err cgi_send_user_manual(struct client_state *csp,
    }
 
    get_string_param(parameters, "file", &filename);
-   /* Check paramter for hack attempts */
-   if (filename && strchr(filename, '/'))
+   if (filename == NULL)
    {
-      return JB_ERR_CGI_PARAMS;
+      /* It's '/' so serve the index.html if there is one.  */
+      filename = "index.html";
    }
-   if (filename && strstr(filename, ".."))
+   else if (NULL != strchr(filename, '/') || NULL != strstr(filename, ".."))
    {
+      /*
+       * We currently only support a flat file
+       * hierarchy for the documentation.
+       */
+      log_error(LOG_LEVEL_ERROR,
+         "Rejecting the request to serve '%s' as it contains '/' or '..'",
+         filename);
       return JB_ERR_CGI_PARAMS;
    }
 
-   full_path = make_path(csp->config->usermanual, filename ? filename : "index.html");
+   full_path = make_path(csp->config->usermanual, filename);
    if (full_path == NULL)
    {
       return JB_ERR_MEMORY;
@@ -726,29 +805,12 @@ jb_err cgi_send_user_manual(struct client_state *csp,
    }
    freez(full_path);
 
-   /* Guess correct Content-Type based on the filename's ending */
-   if (filename)
-   {
-      length = strlen(filename);
-   }
-   else
-   {
-      length = 0;
-   } 
-   if((length>=4) && !strcmp(&filename[length-4], ".css"))
-   {
-      err = enlist(rsp->headers, "Content-Type: text/css");
-   }
-   else if((length>=4) && !strcmp(&filename[length-4], ".jpg"))
-   {
-      err = enlist(rsp->headers, "Content-Type: image/jpeg");
-   }
-   else
-   {
-      err = enlist(rsp->headers, "Content-Type: text/html");
-   }
+   content_type = get_content_type(filename);
+   log_error(LOG_LEVEL_CGI,
+      "Content-Type guessed for %s: %s", filename, content_type);
+
+   return enlist_unique_header(rsp->headers, "Content-Type", content_type);
 
-   return err;
 }
 
 
@@ -1071,7 +1133,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
     * 1) "http://" or "https://" prefix present and followed by URL - OK
     * 2) Only the "http://" or "https://" part is present, no URL - change
     *    to empty string so it will be detected later as "no URL".
-    * 3) Parameter specified but doesn't contain "http(s?)://" - add a
+    * 3) Parameter specified but doesn't start with "http(s?)://" - add a
     *    "http://" prefix.
     * 4) Parameter not specified or is empty string - let this fall through
     *    for now, next block of code will handle it.
@@ -1098,9 +1160,14 @@ jb_err cgi_show_url_info(struct client_state *csp,
          url_param[0] = '\0';
       }
    }
-   else if ((url_param[0] != '\0') && (NULL == strstr(url_param, "://")))
+   else if ((url_param[0] != '\0')
+      && ((NULL == strstr(url_param, "://")
+            || (strstr(url_param, "://") > strstr(url_param, "/")))))
    {
-      /* No prefix - assume http:// */
+      /*
+       * No prefix or at least no prefix before
+       * the first slash - assume http://
+       */
       char *url_param_prefixed = strdup("http://");
 
       if (JB_ERR_OK != string_join(&url_param_prefixed, url_param))
@@ -1491,6 +1558,12 @@ static jb_err show_defines(struct map *exports)
 {
    jb_err err = JB_ERR_OK;
 
+#ifdef FEATURE_ACCEPT_FILTER
+   if (!err) err = map_conditional(exports, "FEATURE_ACCEPT_FILTER", 1);
+#else /* ifndef FEATURE_ACCEPT_FILTER */
+   if (!err) err = map_conditional(exports, "FEATURE_ACCEPT_FILTER", 0);
+#endif /* ndef FEATURE_ACCEPT_FILTER */
+
 #ifdef FEATURE_ACL
    if (!err) err = map_conditional(exports, "FEATURE_ACL", 1);
 #else /* ifndef FEATURE_ACL */
@@ -1503,12 +1576,24 @@ static jb_err show_defines(struct map *exports)
    if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 0);
 #endif /* ndef FEATURE_CGI_EDIT_ACTIONS */
 
+#ifdef FEATURE_COMPRESSION
+   if (!err) err = map_conditional(exports, "FEATURE_COMPRESSION", 1);
+#else /* ifndef FEATURE_COMPRESSION */
+   if (!err) err = map_conditional(exports, "FEATURE_COMPRESSION", 0);
+#endif /* ndef FEATURE_COMPRESSION */
+
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_KEEP_ALIVE", 1);
 #else /* ifndef FEATURE_CONNECTION_KEEP_ALIVE */
    if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_KEEP_ALIVE", 0);
 #endif /* ndef FEATURE_CONNECTION_KEEP_ALIVE */
 
+#ifdef FEATURE_CONNECTION_SHARING
+   if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_SHARING", 1);
+#else /* ifndef FEATURE_CONNECTION_SHARING */
+   if (!err) err = map_conditional(exports, "FEATURE_CONNECTION_SHARING", 0);
+#endif /* ndef FEATURE_CONNECTION_SHARING */
+
 #ifdef FEATURE_FAST_REDIRECTS
    if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 1);
 #else /* ifndef FEATURE_FAST_REDIRECTS */
@@ -1837,6 +1922,7 @@ static jb_err load_file(const char *filename, char **buffer, size_t *length)
    fp = fopen(filename, "rb");
    if (NULL == fp)
    {
+      log_error(LOG_LEVEL_ERROR, "Failed to open %s: %E", filename);
       return JB_ERR_FILE;
    }