LCOV - code coverage report
Current view: top level - fuzz - cgi.c (source / functions) Hit Total Coverage
Test: trace.lcov_info_final Lines: 454 617 73.6 %
Date: 2021-02-22 04:51:02 Functions: 29 32 90.6 %

          Line data    Source code
       1             : /*********************************************************************
       2             :  *
       3             :  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
       4             :  *
       5             :  * Purpose     :  Declares functions to intercept request, generate
       6             :  *                html or gif answers, and to compose HTTP responses.
       7             :  *                This only contains the framework functions, the
       8             :  *                actual handler functions are declared elsewhere.
       9             :  *
      10             :  * Copyright   :  Written by and Copyright (C) 2001-2020
      11             :  *                members of the Privoxy team. https://www.privoxy.org/
      12             :  *
      13             :  *                Based on the Internet Junkbuster originally written
      14             :  *                by and Copyright (C) 1997 Anonymous Coders and
      15             :  *                Junkbusters Corporation.  http://www.junkbusters.com
      16             :  *
      17             :  *                This program is free software; you can redistribute it
      18             :  *                and/or modify it under the terms of the GNU General
      19             :  *                Public License as published by the Free Software
      20             :  *                Foundation; either version 2 of the License, or (at
      21             :  *                your option) any later version.
      22             :  *
      23             :  *                This program is distributed in the hope that it will
      24             :  *                be useful, but WITHOUT ANY WARRANTY; without even the
      25             :  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
      26             :  *                PARTICULAR PURPOSE.  See the GNU General Public
      27             :  *                License for more details.
      28             :  *
      29             :  *                The GNU General Public License should be included with
      30             :  *                this file.  If not, you can view it at
      31             :  *                http://www.gnu.org/copyleft/gpl.html
      32             :  *                or write to the Free Software Foundation, Inc., 59
      33             :  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
      34             :  *
      35             :  **********************************************************************/
      36             : 
      37             : 
      38             : #include "config.h"
      39             : 
      40             : #include <stdio.h>
      41             : #include <sys/types.h>
      42             : #include <stdlib.h>
      43             : #include <ctype.h>
      44             : #include <string.h>
      45             : #include <limits.h>
      46             : #include <assert.h>
      47             : 
      48             : #ifdef FEATURE_COMPRESSION
      49             : #include <zlib.h>
      50             : #endif
      51             : 
      52             : #include "project.h"
      53             : #include "cgi.h"
      54             : #include "list.h"
      55             : #include "encode.h"
      56             : #include "ssplit.h"
      57             : #include "errlog.h"
      58             : #include "filters.h"
      59             : #include "miscutil.h"
      60             : #include "cgisimple.h"
      61             : #include "jbsockets.h"
      62             : #if defined(FEATURE_CGI_EDIT_ACTIONS) || defined(FEATURE_TOGGLE)
      63             : #include "cgiedit.h"
      64             : #endif /* defined(FEATURE_CGI_EDIT_ACTIONS) || defined (FEATURE_TOGGLE) */
      65             : #ifdef FEATURE_HTTPS_INSPECTION
      66             : #include "ssl.h"
      67             : #endif
      68             : 
      69             : /* loadcfg.h is for global_toggle_state only */
      70             : #include "loadcfg.h"
      71             : /* jcc.h is for mutex semaphore globals only */
      72             : #include "jcc.h"
      73             : 
      74             : static char *make_menu(const struct client_state *csp, const char *self);
      75             : 
      76             : /*
      77             :  * List of CGI functions: name, handler, description
      78             :  * Note: Do NOT use single quotes in the description;
      79             :  *       this will break the dynamic "blocked" template!
      80             :  */
      81             : static const struct cgi_dispatcher cgi_dispatchers[] = {
      82             :    { "",
      83             :          cgi_default,
      84             :          "Privoxy main page",
      85             :          TRUE },
      86             : #ifdef FEATURE_GRACEFUL_TERMINATION
      87             :    { "die",
      88             :          cgi_die,
      89             :          "<b>Shut down</b> - <em class=\"warning\">Do not deploy this build in a production environment, "
      90             :         "this is a one click Denial Of Service attack!!!</em>",
      91             :          FALSE },
      92             : #endif
      93             :    { "show-status",
      94             :          cgi_show_status,
      95             : #ifdef FEATURE_CGI_EDIT_ACTIONS
      96             :         "View &amp; change the current configuration",
      97             : #else
      98             :         "View the current configuration",
      99             : #endif
     100             :          TRUE },
     101             : #ifdef FEATURE_CLIENT_TAGS
     102             :    /*
     103             :     * This is marked as harmless because despite the description
     104             :     * used in the menu the actual toggling is done through another
     105             :     * path ("/toggle-client-tag").
     106             :     */
     107             :    { "client-tags",
     108             :          cgi_show_client_tags,
     109             :          "View or toggle the tags that can be set based on the client&#39;s address",
     110             :          TRUE },
     111             : #endif
     112             :    { "show-request",
     113             :          cgi_show_request,
     114             :          "View the request headers",
     115             :          TRUE },
     116             :    { "show-url-info",
     117             :          cgi_show_url_info,
     118             :          "Look up which actions apply to a URL and why",
     119             :          TRUE },
     120             : #ifdef FEATURE_TOGGLE
     121             :    { "toggle",
     122             :          cgi_toggle,
     123             :          "Toggle Privoxy on or off",
     124             :          FALSE },
     125             : #endif /* def FEATURE_TOGGLE */
     126             : #ifdef FEATURE_CLIENT_TAGS
     127             :    { "toggle-client-tag",
     128             :          cgi_toggle_client_tag,
     129             :          NULL,
     130             :          FALSE },
     131             : #endif
     132             : #ifdef FEATURE_CGI_EDIT_ACTIONS
     133             :    { "edit-actions", /* Edit the actions list */
     134             :          cgi_edit_actions,
     135             :          NULL, FALSE },
     136             :    { "eaa", /* Shortcut for edit-actions-add-url-form */
     137             :          cgi_edit_actions_add_url_form,
     138             :          NULL, FALSE },
     139             :    { "eau", /* Shortcut for edit-actions-url-form */
     140             :          cgi_edit_actions_url_form,
     141             :          NULL, FALSE },
     142             :    { "ear", /* Shortcut for edit-actions-remove-url-form */
     143             :          cgi_edit_actions_remove_url_form,
     144             :          NULL, FALSE },
     145             :    { "eal", /* Shortcut for edit-actions-list */
     146             :          cgi_edit_actions_list,
     147             :          NULL, FALSE },
     148             :    { "eafu", /* Shortcut for edit-actions-for-url */
     149             :          cgi_edit_actions_for_url,
     150             :          NULL, FALSE },
     151             :    { "eas", /* Shortcut for edit-actions-submit */
     152             :          cgi_edit_actions_submit,
     153             :          NULL, FALSE },
     154             :    { "easa", /* Shortcut for edit-actions-section-add */
     155             :          cgi_edit_actions_section_add,
     156             :          NULL, FALSE  },
     157             :    { "easr", /* Shortcut for edit-actions-section-remove */
     158             :          cgi_edit_actions_section_remove,
     159             :          NULL, FALSE  },
     160             :    { "eass", /* Shortcut for edit-actions-section-swap */
     161             :          cgi_edit_actions_section_swap,
     162             :          NULL, FALSE  },
     163             :    { "edit-actions-for-url",
     164             :          cgi_edit_actions_for_url,
     165             :          NULL, FALSE  /* Edit the actions for (a) specified URL(s) */ },
     166             :    { "edit-actions-list",
     167             :          cgi_edit_actions_list,
     168             :          NULL, TRUE /* Edit the actions list */ },
     169             :    { "edit-actions-submit",
     170             :          cgi_edit_actions_submit,
     171             :          NULL, FALSE /* Change the actions for (a) specified URL(s) */ },
     172             :    { "edit-actions-url",
     173             :          cgi_edit_actions_url,
     174             :          NULL, FALSE /* Change a URL pattern in the actionsfile */ },
     175             :    { "edit-actions-url-form",
     176             :          cgi_edit_actions_url_form,
     177             :          NULL, FALSE /* Form to change a URL pattern in the actionsfile */ },
     178             :    { "edit-actions-add-url",
     179             :          cgi_edit_actions_add_url,
     180             :          NULL, FALSE /* Add a URL pattern to the actionsfile */ },
     181             :    { "edit-actions-add-url-form",
     182             :          cgi_edit_actions_add_url_form,
     183             :          NULL, FALSE /* Form to add a URL pattern to the actionsfile */ },
     184             :    { "edit-actions-remove-url",
     185             :          cgi_edit_actions_remove_url,
     186             :          NULL, FALSE /* Remove a URL pattern from the actionsfile */ },
     187             :    { "edit-actions-remove-url-form",
     188             :          cgi_edit_actions_remove_url_form,
     189             :          NULL, FALSE /* Form to remove a URL pattern from the actionsfile */ },
     190             :    { "edit-actions-section-add",
     191             :          cgi_edit_actions_section_add,
     192             :          NULL, FALSE /* Remove a section from the actionsfile */ },
     193             :    { "edit-actions-section-remove",
     194             :          cgi_edit_actions_section_remove,
     195             :          NULL, FALSE /* Remove a section from the actionsfile */ },
     196             :    { "edit-actions-section-swap",
     197             :          cgi_edit_actions_section_swap,
     198             :          NULL, FALSE /* Swap two sections in the actionsfile */ },
     199             : #endif /* def FEATURE_CGI_EDIT_ACTIONS */
     200             :    { "error-favicon.ico",
     201             :          cgi_send_error_favicon,
     202             :          NULL, TRUE /* Sends the favicon image for error pages. */ },
     203             :    { "favicon.ico",
     204             :          cgi_send_default_favicon,
     205             :          NULL, TRUE /* Sends the default favicon image. */ },
     206             :    { "robots.txt",
     207             :          cgi_robots_txt,
     208             :          NULL, TRUE /* Sends a robots.txt file to tell robots to go away. */ },
     209             :    { "send-banner",
     210             :          cgi_send_banner,
     211             :          NULL, TRUE /* Send a built-in image */ },
     212             :    { "send-stylesheet",
     213             :          cgi_send_stylesheet,
     214             :          NULL, FALSE /* Send templates/cgi-style.css */ },
     215             :    { "t",
     216             :          cgi_transparent_image,
     217             :          NULL, TRUE /* Send a transparent image (short name) */ },
     218             :    { "url-info-osd.xml",
     219             :          cgi_send_url_info_osd,
     220             :          NULL, TRUE /* Send templates/url-info-osd.xml */ },
     221             :    { "user-manual",
     222             :           cgi_send_user_manual,
     223             :           NULL, TRUE /* Send user-manual */ },
     224             :    { NULL, /* NULL Indicates end of list and default page */
     225             :          cgi_error_404,
     226             :          NULL, TRUE /* Unknown CGI page */ }
     227             : };
     228             : 
     229             : 
     230             : /*
     231             :  * Built-in images for ad replacement
     232             :  *
     233             :  * Hint: You can encode your own images like this:
     234             :  * cat your-image | perl -e 'while (read STDIN, $c, 1) { printf("\\%.3o", unpack("C", $c)); }'
     235             :  */
     236             : 
     237             : #ifdef FEATURE_NO_GIFS
     238             : 
     239             : /*
     240             :  * Checkerboard pattern, as a PNG.
     241             :  */
     242             : const char image_pattern_data[] =
     243             :    "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104"
     244             :    "\122\000\000\000\004\000\000\000\004\010\006\000\000\000\251"
     245             :    "\361\236\176\000\000\000\006\142\113\107\104\000\000\000\000"
     246             :    "\000\000\371\103\273\177\000\000\000\033\111\104\101\124\010"
     247             :    "\327\143\140\140\140\060\377\377\377\077\003\234\106\341\060"
     248             :    "\060\230\063\020\124\001\000\161\021\031\241\034\364\030\143"
     249             :    "\000\000\000\000\111\105\116\104\256\102\140\202";
     250             : 
     251             : /*
     252             :  * 1x1 transparent PNG.
     253             :  */
     254             : const char image_blank_data[] =
     255             :  "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104\122"
     256             :  "\000\000\000\001\000\000\000\001\001\003\000\000\000\045\333\126"
     257             :  "\312\000\000\000\003\120\114\124\105\377\377\377\247\304\033\310"
     258             :  "\000\000\000\001\164\122\116\123\000\100\346\330\146\000\000\000"
     259             :  "\001\142\113\107\104\000\210\005\035\110\000\000\000\012\111\104"
     260             :  "\101\124\170\001\143\140\000\000\000\002\000\001\163\165\001\030"
     261             :  "\000\000\000\000\111\105\116\104\256\102\140\202";
     262             : #else
     263             : 
     264             : /*
     265             :  * Checkerboard pattern, as a GIF.
     266             :  */
     267             : const char image_pattern_data[] =
     268             :    "\107\111\106\070\071\141\004\000\004\000\200\000\000\310\310"
     269             :    "\310\377\377\377\041\376\016\111\040\167\141\163\040\141\040"
     270             :    "\142\141\156\156\145\162\000\041\371\004\001\012\000\001\000"
     271             :    "\054\000\000\000\000\004\000\004\000\000\002\005\104\174\147"
     272             :    "\270\005\000\073";
     273             : 
     274             : /*
     275             :  * 1x1 transparent GIF.
     276             :  */
     277             : const char image_blank_data[] =
     278             :    "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000"
     279             :    "\000!\371\004\001\000\000\000\000,\000\000\000\000\001"
     280             :    "\000\001\000\000\002\002D\001\000;";
     281             : #endif
     282             : 
     283             : const size_t image_pattern_length = sizeof(image_pattern_data) - 1;
     284             : const size_t image_blank_length   = sizeof(image_blank_data) - 1;
     285             : 
     286             : #ifdef FEATURE_COMPRESSION
     287             : /*
     288             :  * Minimum length which a buffer has to reach before
     289             :  * we bother to (re-)compress it. Completely arbitrary.
     290             :  */
     291             : const size_t LOWER_LENGTH_LIMIT_FOR_COMPRESSION = 1024U;
     292             : #endif
     293             : 
     294             : static struct http_response cgi_error_memory_response[1];
     295             : 
     296             : static struct http_response *dispatch_known_cgi(struct client_state * csp,
     297             :                                                 const char * path);
     298             : static struct map *parse_cgi_parameters(char *argstring);
     299             : 
     300             : 
     301             : /*********************************************************************
     302             :  *
     303             :  * Function    :  dispatch_cgi
     304             :  *
     305             :  * Description :  Checks if a request URL has either the magical
     306             :  *                hostname CGI_SITE_1_HOST (usually http://p.p/) or
     307             :  *                matches CGI_SITE_2_HOST CGI_SITE_2_PATH (usually
     308             :  *                http://config.privoxy.org/). If so, it passes
     309             :  *                the (rest of the) path onto dispatch_known_cgi, which
     310             :  *                calls the relevant CGI handler function.
     311             :  *
     312             :  * Parameters  :
     313             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     314             :  *
     315             :  * Returns     :  http_response if match, NULL if nonmatch or handler fail
     316             :  *
     317             :  *********************************************************************/
     318       45478 : struct http_response *dispatch_cgi(struct client_state *csp)
     319             : {
     320       45478 :    const char *host = csp->http->host;
     321       45478 :    const char *path = csp->http->path;
     322             : 
     323             :    /*
     324             :     * Should we intercept ?
     325             :     */
     326             : 
     327             :    /* Note: "example.com" and "example.com." are equivalent hostnames. */
     328             : 
     329             :    /* Either the host matches CGI_SITE_1_HOST ..*/
     330       45478 :    if (   ( (0 == strcmpic(host, CGI_SITE_1_HOST))
     331       38018 :          || (0 == strcmpic(host, CGI_SITE_1_HOST ".")))
     332        7846 :        && (path[0] == '/'))
     333             :    {
     334             :       /* ..then the path will all be for us.  Remove leading '/' */
     335        7835 :       path++;
     336             :    }
     337             :    /* Or it's the host part CGI_SITE_2_HOST, and the path CGI_SITE_2_PATH */
     338       37643 :    else if ((  (0 == strcmpic(host, CGI_SITE_2_HOST))
     339       23318 :             || (0 == strcmpic(host, CGI_SITE_2_HOST ".")))
     340       14389 :           && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))))
     341             :    {
     342             :       /* take everything following CGI_SITE_2_PATH */
     343       14389 :       path += strlen(CGI_SITE_2_PATH);
     344       14389 :       if (*path == '/')
     345             :       {
     346             :          /* skip the forward slash after CGI_SITE_2_PATH */
     347       14384 :          path++;
     348             :       }
     349           5 :       else if (*path != '\0')
     350             :       {
     351             :          /*
     352             :           * weirdness: URL is /configXXX, where XXX is some string
     353             :           * Do *NOT* intercept.
     354             :           */
     355           5 :          return NULL;
     356             :       }
     357             :    }
     358             :    else
     359             :    {
     360             :       /* Not a CGI */
     361       23254 :       return NULL;
     362             :    }
     363             : 
     364       22219 :    if (strcmpic(csp->http->gpc, "GET")
     365        3924 :     && strcmpic(csp->http->gpc, "HEAD"))
     366             :    {
     367        3651 :       log_error(LOG_LEVEL_ERROR,
     368             :          "CGI request with unsupported method received: %s", csp->http->gpc);
     369             :       /*
     370             :        * The CGI pages currently only support GET and HEAD requests.
     371             :        *
     372             :        * If the client used a different method, ditch any data following
     373             :        * the current headers to reduce the likelihood of parse errors
     374             :        * with the following request.
     375             :        */
     376        3651 :       csp->client_iob->eod = csp->client_iob->cur;
     377             :    }
     378             : 
     379             :    /*
     380             :     * This is a CGI call.
     381             :     */
     382             : 
     383       22219 :    return dispatch_known_cgi(csp, path);
     384             : }
     385             : 
     386             : 
     387             : /*********************************************************************
     388             :  *
     389             :  * Function    :  grep_cgi_referrer
     390             :  *
     391             :  * Description :  Ugly provisorical fix that greps the value of the
     392             :  *                referer HTTP header field out of a linked list of
     393             :  *                strings like found at csp->headers. Will disappear
     394             :  *                in Privoxy 3.1.
     395             :  *
     396             :  *                FIXME: csp->headers ought to be csp->http->headers
     397             :  *                FIXME: Parsing all client header lines should
     398             :  *                       happen right after the request is received!
     399             :  *
     400             :  * Parameters  :
     401             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     402             :  *
     403             :  * Returns     :  pointer to value (no copy!), or NULL if none found.
     404             :  *
     405             :  *********************************************************************/
     406        5823 : static char *grep_cgi_referrer(const struct client_state *csp)
     407             : {
     408             :    struct list_entry *p;
     409        5823 :    struct list_entry *first_header =
     410             : #ifdef FEATURE_HTTPS_INSPECTION
     411        5823 :       client_use_ssl(csp) ? csp->https_headers->first :
     412             : #endif
     413             :       csp->headers->first;
     414             : 
     415       43097 :    for (p = first_header; p != NULL; p = p->next)
     416             :    {
     417       41523 :       if (p->str == NULL) continue;
     418       41523 :       if (strncmpic(p->str, "Referer: ", 9) == 0)
     419             :       {
     420        4249 :          return ((p->str) + 9);
     421             :       }
     422             :    }
     423        1574 :    return NULL;
     424             : 
     425             : }
     426             : 
     427             : 
     428             : /*********************************************************************
     429             :  *
     430             :  * Function    :  referrer_is_safe
     431             :  *
     432             :  * Description :  Decides whether we trust the Referer for
     433             :  *                CGI pages which are only meant to be reachable
     434             :  *                through Privoxy's web interface directly.
     435             :  *
     436             :  * Parameters  :
     437             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     438             :  *
     439             :  * Returns     :  TRUE  if the referrer is safe, or
     440             :  *                FALSE if the referrer is unsafe or not set.
     441             :  *
     442             :  *********************************************************************/
     443        5823 : static int referrer_is_safe(const struct client_state *csp)
     444             : {
     445             :    char *referrer;
     446             :    static const char alternative_prefix[] = "http://" CGI_SITE_1_HOST "/";
     447             : #ifdef FEATURE_HTTPS_INSPECTION
     448             :    static const char alt_prefix_https[] = "https://" CGI_SITE_1_HOST "/";
     449             : #endif
     450        5823 :    const char *trusted_cgi_referrer = csp->config->trusted_cgi_referrer;
     451             : 
     452        5823 :    referrer = grep_cgi_referrer(csp);
     453             : 
     454        5823 :    if (NULL == referrer)
     455             :    {
     456             :       /* No referrer, no access  */
     457        1574 :       log_error(LOG_LEVEL_ERROR, "Denying access to %s. No referrer found.",
     458             :          csp->http->url);
     459             :    }
     460        4249 :    else if ((0 == strncmp(referrer, CGI_PREFIX_HTTP, sizeof(CGI_PREFIX_HTTP)-1))
     461             : #ifdef FEATURE_HTTPS_INSPECTION
     462         585 :          || (0 == strncmp(referrer, CGI_PREFIX_HTTPS, sizeof(CGI_PREFIX_HTTPS)-1))
     463         565 :          || (0 == strncmp(referrer, alt_prefix_https, strlen(alt_prefix_https)))
     464             : #endif
     465         530 :          || (0 == strncmp(referrer, alternative_prefix, strlen(alternative_prefix))))
     466             :    {
     467             :       /* Trustworthy referrer */
     468        3730 :       log_error(LOG_LEVEL_CGI, "Granting access to %s, referrer %s is trustworthy.",
     469             :          csp->http->url, referrer);
     470             : 
     471        3730 :       return TRUE;
     472             :    }
     473         519 :    else if ((trusted_cgi_referrer != NULL) && (0 == strncmp(referrer,
     474             :             trusted_cgi_referrer, strlen(trusted_cgi_referrer))))
     475             :    {
     476             :       /*
     477             :        * After some more testing this block should be merged with
     478             :        * the previous one or the log level should bedowngraded.
     479             :        */
     480          22 :       log_error(LOG_LEVEL_INFO, "Granting access to %s based on trusted referrer %s",
     481             :          csp->http->url, referrer);
     482             : 
     483          22 :       return TRUE;
     484             :    }
     485             :    else
     486             :    {
     487             :       /* Untrustworthy referrer */
     488         497 :       log_error(LOG_LEVEL_ERROR, "Denying access to %s, referrer %s isn't trustworthy.",
     489             :          csp->http->url, referrer);
     490             :    }
     491             : 
     492        2071 :    return FALSE;
     493             : 
     494             : }
     495             : 
     496             : /*********************************************************************
     497             :  *
     498             :  * Function    :  dispatch_known_cgi
     499             :  *
     500             :  * Description :  Processes a CGI once dispatch_cgi has determined that
     501             :  *                it matches one of the magic prefixes. Parses the path
     502             :  *                as a cgi name plus query string, prepares a map that
     503             :  *                maps CGI parameter names to their values, initializes
     504             :  *                the http_response struct, and calls the relevant CGI
     505             :  *                handler function.
     506             :  *
     507             :  * Parameters  :
     508             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     509             :  *          2  :  path = Path of CGI, with the CGI prefix removed.
     510             :  *                       Should not have a leading "/".
     511             :  *
     512             :  * Returns     :  http_response, or NULL on handler failure or out of
     513             :  *                memory.
     514             :  *
     515             :  *********************************************************************/
     516       22219 : static struct http_response *dispatch_known_cgi(struct client_state * csp,
     517             :                                                 const char * path)
     518             : {
     519             :    const struct cgi_dispatcher *d;
     520             :    struct map *param_list;
     521             :    struct http_response *rsp;
     522             :    char *query_args_start;
     523             :    char *path_copy;
     524             :    jb_err err;
     525             : 
     526       22219 :    if (NULL == (path_copy = strdup(path)))
     527             :    {
     528           0 :       return cgi_error_memory();
     529             :    }
     530       22219 :    query_args_start = path_copy;
     531      283445 :    while (*query_args_start && *query_args_start != '?' && *query_args_start != '/')
     532             :    {
     533      261226 :       query_args_start++;
     534             :    }
     535       22219 :    if (*query_args_start == '/')
     536             :    {
     537        1123 :       *query_args_start++ = '\0';
     538        1123 :       param_list = new_map();
     539        1123 :       err = map(param_list, "file", 1, url_decode(query_args_start), 0);
     540        1123 :       if (JB_ERR_OK != err)
     541             :       {
     542           0 :          free(param_list);
     543           0 :          free(path_copy);
     544           0 :          return cgi_error_memory();
     545             :       }
     546             :    }
     547             :    else
     548             :    {
     549       21096 :       if (*query_args_start == '?')
     550             :       {
     551       12665 :          *query_args_start++ = '\0';
     552             :       }
     553       21096 :       if (NULL == (param_list = parse_cgi_parameters(query_args_start)))
     554             :       {
     555           0 :          free(path_copy);
     556           0 :          return cgi_error_memory();
     557             :       }
     558             :    }
     559             : 
     560             :    /*
     561             :     * At this point:
     562             :     * path_copy        = CGI call name
     563             :     * param_list       = CGI params, as map
     564             :     */
     565             : 
     566             :    /* Get mem for response or fail*/
     567       22219 :    if (NULL == (rsp = alloc_http_response()))
     568             :    {
     569           0 :       free(path_copy);
     570           0 :       free_map(param_list);
     571           0 :       return cgi_error_memory();
     572             :    }
     573             : 
     574             :    /*
     575             :     * Find and start the right CGI function
     576             :     */
     577       22219 :    d = cgi_dispatchers;
     578             :    for (;;)
     579             :    {
     580      376700 :       if ((d->name == NULL) || (strcmp(path_copy, d->name) == 0))
     581             :       {
     582             :          /*
     583             :           * If the called CGI is either harmless, or referred
     584             :           * from a trusted source, start it.
     585             :           */
     586       22219 :          if (d->harmless || referrer_is_safe(csp))
     587             :          {
     588       20148 :             err = (d->handler)(csp, rsp, param_list);
     589             :          }
     590             :          else
     591             :          {
     592             :             /*
     593             :              * Else, modify toggle calls so that they only display
     594             :              * the status, and deny all other calls.
     595             :              */
     596        2071 :             if (0 == strcmp(path_copy, "toggle"))
     597             :             {
     598         246 :                unmap(param_list, "set");
     599         246 :                err = (d->handler)(csp, rsp, param_list);
     600             :             }
     601             :             else
     602             :             {
     603        1825 :                err = cgi_error_disabled(csp, rsp);
     604             :             }
     605             :          }
     606             : 
     607       22219 :          free(path_copy);
     608       22219 :          free_map(param_list);
     609             : 
     610       22219 :          if (err == JB_ERR_CGI_PARAMS)
     611             :          {
     612        1941 :             err = cgi_error_bad_param(csp, rsp);
     613             :          }
     614       22219 :          if (err && (err != JB_ERR_MEMORY))
     615             :          {
     616             :             /* Unexpected error! Shouldn't get here */
     617           0 :             log_error(LOG_LEVEL_ERROR,
     618             :                "Unexpected CGI error %d in top-level handler. "
     619             :                "Please file a bug report!", err);
     620           0 :             err = cgi_error_unknown(csp, rsp, err);
     621             :          }
     622       22219 :          if (!err)
     623             :          {
     624             :             /* It worked */
     625       22219 :             rsp->crunch_reason = CGI_CALL;
     626       22219 :             return finish_http_response(csp, rsp);
     627             :          }
     628             :          else
     629             :          {
     630             :             /* Error in handler, probably out-of-memory */
     631           0 :             free_http_response(rsp);
     632           0 :             return cgi_error_memory();
     633             :          }
     634             :       }
     635      354481 :       d++;
     636             :    }
     637             : }
     638             : 
     639             : 
     640             : /*********************************************************************
     641             :  *
     642             :  * Function    :  parse_cgi_parameters
     643             :  *
     644             :  * Description :  Parse a URL-encoded argument string into name/value
     645             :  *                pairs and store them in a struct map list.
     646             :  *
     647             :  * Parameters  :
     648             :  *          1  :  argstring = string to be parsed.  Will be trashed.
     649             :  *
     650             :  * Returns     :  pointer to param list, or NULL if out of memory.
     651             :  *
     652             :  *********************************************************************/
     653       21096 : static struct map *parse_cgi_parameters(char *argstring)
     654             : {
     655             :    char *p;
     656             :    char **vector;
     657             :    int pairs, i;
     658             :    struct map *cgi_params;
     659             : 
     660             :    /*
     661             :     * XXX: This estimate is guaranteed to be high enough as we
     662             :     *      let ssplit() ignore empty fields, but also a bit wasteful.
     663             :     *      The same hack is used in get_last_url() so it looks like
     664             :     *      a real solution is needed.
     665             :     */
     666       21096 :    size_t max_segments = strlen(argstring) / 2 + 1;
     667       21096 :    vector = malloc_or_die(max_segments * sizeof(char *));
     668             : 
     669       21096 :    cgi_params = new_map();
     670             : 
     671             :    /*
     672             :     * IE 5 does, of course, violate RFC 2316 Sect 4.1 and sends
     673             :     * the fragment identifier along with the request, so we must
     674             :     * cut it off here, so it won't pollute the CGI params:
     675             :     */
     676       21096 :    if (NULL != (p = strchr(argstring, '#')))
     677             :    {
     678         249 :       *p = '\0';
     679             :    }
     680             : 
     681       21096 :    pairs = ssplit(argstring, "&", vector, max_segments);
     682       21096 :    assert(pairs != -1);
     683       21096 :    if (pairs == -1)
     684             :    {
     685           0 :       freez(vector);
     686           0 :       free_map(cgi_params);
     687           0 :       return NULL;
     688             :    }
     689             : 
     690      104341 :    for (i = 0; i < pairs; i++)
     691             :    {
     692       83245 :       if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
     693             :       {
     694       77540 :          *p = '\0';
     695       77540 :          if (map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0))
     696             :          {
     697           0 :             freez(vector);
     698           0 :             free_map(cgi_params);
     699           0 :             return NULL;
     700             :          }
     701             :       }
     702             :    }
     703             : 
     704       21096 :    freez(vector);
     705             : 
     706       21096 :    return cgi_params;
     707             : 
     708             : }
     709             : 
     710             : 
     711             : /*********************************************************************
     712             :  *
     713             :  * Function    :  get_char_param
     714             :  *
     715             :  * Description :  Get a single-character parameter passed to a CGI
     716             :  *                function.
     717             :  *
     718             :  * Parameters  :
     719             :  *          1  :  parameters = map of cgi parameters
     720             :  *          2  :  param_name = The name of the parameter to read
     721             :  *
     722             :  * Returns     :  Uppercase character on success, '\0' on error.
     723             :  *
     724             :  *********************************************************************/
     725       32530 : char get_char_param(const struct map *parameters,
     726             :                     const char *param_name)
     727             : {
     728             :    char ch;
     729             : 
     730       32530 :    assert(parameters);
     731       32530 :    assert(param_name);
     732             : 
     733       32530 :    ch = *(lookup(parameters, param_name));
     734       32530 :    if ((ch >= 'a') && (ch <= 'z'))
     735             :    {
     736         247 :       ch = (char)(ch - 'a' + 'A');
     737             :    }
     738             : 
     739       32530 :    return ch;
     740             : }
     741             : 
     742             : 
     743             : /*********************************************************************
     744             :  *
     745             :  * Function    :  get_string_param
     746             :  *
     747             :  * Description :  Get a string parameter, to be used as an
     748             :  *                ACTION_STRING or ACTION_MULTI parameter.
     749             :  *                Validates the input to prevent stupid/malicious
     750             :  *                users from corrupting their action file.
     751             :  *
     752             :  * Parameters  :
     753             :  *          1  :  parameters = map of cgi parameters
     754             :  *          2  :  param_name = The name of the parameter to read
     755             :  *          3  :  pparam = destination for parameter.  Allocated as
     756             :  *                part of the map "parameters", so don't free it.
     757             :  *                Set to NULL if not specified.
     758             :  *
     759             :  * Returns     :  JB_ERR_OK         on success, or if the parameter
     760             :  *                                  was not specified.
     761             :  *                JB_ERR_MEMORY     on out-of-memory.
     762             :  *                JB_ERR_CGI_PARAMS if the parameter is not valid.
     763             :  *
     764             :  *********************************************************************/
     765        9935 : jb_err get_string_param(const struct map *parameters,
     766             :                         const char *param_name,
     767             :                         const char **pparam)
     768             : {
     769             :    const char *param;
     770             :    const char *s;
     771             :    char ch;
     772             : 
     773        9935 :    assert(parameters);
     774        9935 :    assert(param_name);
     775        9935 :    assert(pparam);
     776             : 
     777        9935 :    *pparam = NULL;
     778             : 
     779        9935 :    param = lookup(parameters, param_name);
     780        9935 :    if (!*param)
     781             :    {
     782        7929 :       return JB_ERR_OK;
     783             :    }
     784             : 
     785        2006 :    if (strlen(param) >= CGI_PARAM_LEN_MAX)
     786             :    {
     787             :       /*
     788             :        * Too long.
     789             :        *
     790             :        * Note that the length limit is arbitrary, it just seems
     791             :        * sensible to limit it to *something*.  There's no
     792             :        * technical reason for any limit at all.
     793             :        */
     794          89 :       return JB_ERR_CGI_PARAMS;
     795             :    }
     796             : 
     797             :    /* Check every character to see if it's legal */
     798        1917 :    s = param;
     799       32707 :    while ((ch = *s++) != '\0')
     800             :    {
     801       30967 :       if (((unsigned char)ch < (unsigned char)' ')
     802       30797 :         || (ch == '}'))
     803             :       {
     804             :          /* Probable hack attempt, or user accidentally used '}'. */
     805         177 :          return JB_ERR_CGI_PARAMS;
     806             :       }
     807             :    }
     808             : 
     809             :    /* Success */
     810        1740 :    *pparam = param;
     811             : 
     812        1740 :    return JB_ERR_OK;
     813             : }
     814             : 
     815             : 
     816             : /*********************************************************************
     817             :  *
     818             :  * Function    :  get_number_param
     819             :  *
     820             :  * Description :  Get a non-negative integer from the parameters
     821             :  *                passed to a CGI function.
     822             :  *
     823             :  * Parameters  :
     824             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     825             :  *          2  :  parameters = map of cgi parameters
     826             :  *          3  :  name = Name of CGI parameter to read
     827             :  *          4  :  pvalue = destination for value.
     828             :  *                         Set to -1 on error.
     829             :  *
     830             :  * Returns     :  JB_ERR_OK         on success
     831             :  *                JB_ERR_MEMORY     on out-of-memory
     832             :  *                JB_ERR_CGI_PARAMS if the parameter was not specified
     833             :  *                                  or is not valid.
     834             :  *
     835             :  *********************************************************************/
     836        7986 : jb_err get_number_param(struct client_state *csp,
     837             :                         const struct map *parameters,
     838             :                         char *name,
     839             :                         unsigned *pvalue)
     840             : {
     841             :    const char *param;
     842             :    char *endptr;
     843             : 
     844        7986 :    assert(csp);
     845        7986 :    assert(parameters);
     846        7986 :    assert(name);
     847        7986 :    assert(pvalue);
     848             : 
     849        7986 :    *pvalue = 0;
     850             : 
     851        7986 :    param = lookup(parameters, name);
     852        7986 :    if (!*param)
     853             :    {
     854        1371 :       return JB_ERR_CGI_PARAMS;
     855             :    }
     856             : 
     857        6615 :    *pvalue = (unsigned int)strtol(param, &endptr, 0);
     858        6615 :    if (*endptr != '\0')
     859             :    {
     860         703 :       return JB_ERR_CGI_PARAMS;
     861             :    }
     862             : 
     863        5912 :    return JB_ERR_OK;
     864             : 
     865             : }
     866             : 
     867             : 
     868             : /*********************************************************************
     869             :  *
     870             :  * Function    :  error_response
     871             :  *
     872             :  * Description :  returns an http_response that explains the reason
     873             :  *                why a request failed.
     874             :  *
     875             :  * Parameters  :
     876             :  *          1  :  csp = Current client state (buffers, headers, etc...)
     877             :  *          2  :  templatename = Which template should be used for the answer
     878             :  *
     879             :  * Returns     :  A http_response.  If we run out of memory, this
     880             :  *                will be cgi_error_memory().
     881             :  *
     882             :  *********************************************************************/
     883         108 : struct http_response *error_response(struct client_state *csp,
     884             :                                      const char *templatename)
     885             : {
     886             :    jb_err err;
     887             :    struct http_response *rsp;
     888         108 :    struct map *exports = default_exports(csp, NULL);
     889         108 :    char *path = NULL;
     890             : 
     891         108 :    if (exports == NULL)
     892             :    {
     893           0 :       return cgi_error_memory();
     894             :    }
     895             : 
     896         108 :    if (NULL == (rsp = alloc_http_response()))
     897             :    {
     898           0 :       free_map(exports);
     899           0 :       return cgi_error_memory();
     900             :    }
     901             : 
     902             : #ifdef FEATURE_FORCE_LOAD
     903         108 :    if (csp->flags & CSP_FLAG_FORCED)
     904             :    {
     905           3 :       path = strdup(FORCE_PREFIX);
     906             :    }
     907             :    else
     908             : #endif /* def FEATURE_FORCE_LOAD */
     909             :    {
     910         105 :       path = strdup("");
     911             :    }
     912         108 :    err = string_append(&path, csp->http->path);
     913             : 
     914         108 :    if (!err) err = map(exports, "host", 1, html_encode(csp->http->host), 0);
     915         108 :    if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
     916         108 :    if (!err) err = map(exports, "path", 1, html_encode_and_free_original(path), 0);
     917         108 :    if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
     918         108 :    if (!err)
     919             :    {
     920         108 :      err = map(exports, "host-ip", 1, html_encode(csp->http->host_ip_addr_str), 0);
     921         108 :      if (err)
     922             :      {
     923             :        /* Some failures, like "404 no such domain", don't have an IP address. */
     924         108 :        err = map(exports, "host-ip", 1, html_encode(csp->http->host), 0);
     925             :      }
     926             :    }
     927             : 
     928             : 
     929         108 :    if (err)
     930             :    {
     931           0 :       free_map(exports);
     932           0 :       free_http_response(rsp);
     933           0 :       return cgi_error_memory();
     934             :    }
     935             : 
     936         108 :    if (!strcmp(templatename, "no-such-domain"))
     937             :    {
     938           0 :       rsp->status = strdup("404 No such domain");
     939           0 :       rsp->crunch_reason = NO_SUCH_DOMAIN;
     940             :    }
     941         108 :    else if (!strcmp(templatename, "forwarding-failed"))
     942             :    {
     943           0 :       const struct forward_spec *fwd = forward_url(csp, csp->http);
     944           0 :       char *socks_type = NULL;
     945           0 :       if (fwd == NULL)
     946             :       {
     947           0 :          log_error(LOG_LEVEL_FATAL, "gateway spec is NULL. This shouldn't happen!");
     948             :          /* Never get here - LOG_LEVEL_FATAL causes program exit */
     949             :       }
     950             : 
     951             :       /*
     952             :        * XXX: While the template is called forwarding-failed,
     953             :        * it currently only handles socks forwarding failures.
     954             :        */
     955           0 :       assert(fwd != NULL);
     956           0 :       assert(fwd->type != SOCKS_NONE);
     957             : 
     958             :       /*
     959             :        * Map failure reason, forwarding type and forwarder.
     960             :        */
     961           0 :       if (NULL == csp->error_message)
     962             :       {
     963             :          /*
     964             :           * Either we forgot to record the failure reason,
     965             :           * or the memory allocation failed.
     966             :           */
     967           0 :          log_error(LOG_LEVEL_ERROR, "Socks failure reason missing.");
     968           0 :          csp->error_message = strdup("Failure reason missing. Check the log file for details.");
     969             :       }
     970           0 :       if (!err) err = map(exports, "gateway", 1, fwd->gateway_host, 1);
     971             : 
     972             :       /*
     973             :        * XXX: this is almost the same code as in cgi_show_url_info()
     974             :        * and thus should be factored out and shared.
     975             :        */
     976           0 :       switch (fwd->type)
     977             :       {
     978           0 :          case SOCKS_4:
     979           0 :             socks_type = "socks4-";
     980           0 :             break;
     981           0 :          case SOCKS_4A:
     982           0 :             socks_type = "socks4a-";
     983           0 :             break;
     984           0 :          case SOCKS_5:
     985           0 :             socks_type = "socks5-";
     986           0 :             break;
     987           0 :          case SOCKS_5T:
     988           0 :             socks_type = "socks5t-";
     989           0 :             break;
     990           0 :          case FORWARD_WEBSERVER:
     991           0 :             socks_type = "webserver-";
     992           0 :             break;
     993           0 :          default:
     994           0 :             log_error(LOG_LEVEL_FATAL, "Unknown socks type: %d.", fwd->type);
     995             :       }
     996             : 
     997           0 :       if (!err) err = map(exports, "forwarding-type", 1, socks_type, 1);
     998           0 :       if (!err) err = map(exports, "error-message", 1, html_encode(csp->error_message), 0);
     999           0 :       if ((NULL == csp->error_message) || err)
    1000             :       {
    1001           0 :          free_map(exports);
    1002           0 :          free_http_response(rsp);
    1003           0 :          return cgi_error_memory();
    1004             :       }
    1005             : 
    1006           0 :       rsp->status = strdup("503 Forwarding failure");
    1007           0 :       rsp->crunch_reason = FORWARDING_FAILED;
    1008             :    }
    1009         108 :    else if (!strcmp(templatename, "connect-failed"))
    1010             :    {
    1011          41 :       rsp->status = strdup("503 Connect failed");
    1012          41 :       rsp->crunch_reason = CONNECT_FAILED;
    1013             :    }
    1014          67 :    else if (!strcmp(templatename, "connection-timeout"))
    1015             :    {
    1016           0 :       rsp->status = strdup("504 Connection timeout");
    1017           0 :       rsp->crunch_reason = CONNECTION_TIMEOUT;
    1018             :    }
    1019          67 :    else if (!strcmp(templatename, "no-server-data"))
    1020             :    {
    1021          67 :       rsp->status = strdup("502 No data received from server or forwarder");
    1022          67 :       rsp->crunch_reason = NO_SERVER_DATA;
    1023             :    }
    1024             : 
    1025         108 :    if (rsp->status == NULL)
    1026             :    {
    1027           0 :       free_map(exports);
    1028           0 :       free_http_response(rsp);
    1029           0 :       return cgi_error_memory();
    1030             :    }
    1031             : 
    1032         108 :    err = template_fill_for_cgi(csp, templatename, exports, rsp);
    1033         108 :    if (err)
    1034             :    {
    1035           0 :       free_http_response(rsp);
    1036           0 :       return cgi_error_memory();
    1037             :    }
    1038             : 
    1039         108 :    return finish_http_response(csp, rsp);
    1040             : }
    1041             : 
    1042             : 
    1043             : /*********************************************************************
    1044             :  *
    1045             :  * Function    :  cgi_error_disabled
    1046             :  *
    1047             :  * Description :  CGI function that is called to generate an error
    1048             :  *                response if the actions editor or toggle CGI are
    1049             :  *                accessed despite having being disabled at compile-
    1050             :  *                or run-time, or if the user followed an untrusted link
    1051             :  *                to access a unsafe CGI feature that is only reachable
    1052             :  *                through Privoxy directly.
    1053             :  *
    1054             :  * Parameters  :
    1055             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1056             :  *          2  :  rsp = http_response data structure for output
    1057             :  *
    1058             :  * CGI Parameters : none
    1059             :  *
    1060             :  * Returns     :  JB_ERR_OK on success
    1061             :  *                JB_ERR_MEMORY on out-of-memory error.
    1062             :  *
    1063             :  *********************************************************************/
    1064        1825 : jb_err cgi_error_disabled(const struct client_state *csp,
    1065             :                           struct http_response *rsp)
    1066             : {
    1067             :    struct map *exports;
    1068             : 
    1069        1825 :    assert(csp);
    1070        1825 :    assert(rsp);
    1071             : 
    1072        1825 :    rsp->status = strdup_or_die("403 Request not trusted or feature disabled");
    1073             : 
    1074        1825 :    if (NULL == (exports = default_exports(csp, "cgi-error-disabled")))
    1075             :    {
    1076           0 :       return JB_ERR_MEMORY;
    1077             :    }
    1078        1825 :    if (map(exports, "url", 1, html_encode(csp->http->url), 0))
    1079             :    {
    1080             :       /* Not important enough to do anything */
    1081           0 :       log_error(LOG_LEVEL_ERROR, "Failed to fill in url.");
    1082             :    }
    1083             : 
    1084        1825 :    return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
    1085             : }
    1086             : 
    1087             : 
    1088             : /*********************************************************************
    1089             :  *
    1090             :  * Function    :  cgi_init_error_messages
    1091             :  *
    1092             :  * Description :  Call at the start of the program to initialize
    1093             :  *                the error message used by cgi_error_memory().
    1094             :  *
    1095             :  * Parameters  :  N/A
    1096             :  *
    1097             :  * Returns     :  N/A
    1098             :  *
    1099             :  *********************************************************************/
    1100        3098 : void cgi_init_error_messages(void)
    1101             : {
    1102        3098 :    memset(cgi_error_memory_response, '\0', sizeof(*cgi_error_memory_response));
    1103        3098 :    cgi_error_memory_response->head =
    1104             :       "HTTP/1.0 500 Internal Privoxy Error\r\n"
    1105             :       "Content-Type: text/html\r\n"
    1106             :       "\r\n";
    1107        3098 :    cgi_error_memory_response->body =
    1108             :       "<html>\n"
    1109             :       "<head>\n"
    1110             :       " <title>500 Internal Privoxy Error</title>\n"
    1111             :       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
    1112             :       "</head>\n"
    1113             :       "<body>\n"
    1114             :       "<h1>500 Internal Privoxy Error</h1>\n"
    1115             :       "<p>Privoxy <b>ran out of memory</b> while processing your request.</p>\n"
    1116             :       "<p>Please contact your proxy administrator, or try again later</p>\n"
    1117             :       "</body>\n"
    1118             :       "</html>\n";
    1119             : 
    1120        3098 :    cgi_error_memory_response->head_length =
    1121        3098 :       strlen(cgi_error_memory_response->head);
    1122        3098 :    cgi_error_memory_response->content_length =
    1123        3098 :       strlen(cgi_error_memory_response->body);
    1124        3098 :    cgi_error_memory_response->crunch_reason = OUT_OF_MEMORY;
    1125        3098 : }
    1126             : 
    1127             : 
    1128             : /*********************************************************************
    1129             :  *
    1130             :  * Function    :  cgi_error_memory
    1131             :  *
    1132             :  * Description :  Called if a CGI function runs out of memory.
    1133             :  *                Returns a statically-allocated error response.
    1134             :  *
    1135             :  * Parameters  :  N/A
    1136             :  *
    1137             :  * Returns     :  http_response data structure for output.  This is
    1138             :  *                statically allocated, for obvious reasons.
    1139             :  *
    1140             :  *********************************************************************/
    1141           0 : struct http_response *cgi_error_memory(void)
    1142             : {
    1143             :    /* assert that it's been initialized. */
    1144           0 :    assert(cgi_error_memory_response->head);
    1145             : 
    1146           0 :    return cgi_error_memory_response;
    1147             : }
    1148             : 
    1149             : 
    1150             : /*********************************************************************
    1151             :  *
    1152             :  * Function    :  cgi_error_no_template
    1153             :  *
    1154             :  * Description :  Almost-CGI function that is called if a template
    1155             :  *                cannot be loaded.  Note this is not a true CGI,
    1156             :  *                it takes a template name rather than a map of
    1157             :  *                parameters.
    1158             :  *
    1159             :  * Parameters  :
    1160             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1161             :  *          2  :  rsp = http_response data structure for output
    1162             :  *          3  :  template_name = Name of template that could not
    1163             :  *                                be loaded.
    1164             :  *
    1165             :  * Returns     :  JB_ERR_OK on success
    1166             :  *                JB_ERR_MEMORY on out-of-memory error.
    1167             :  *
    1168             :  *********************************************************************/
    1169         335 : jb_err cgi_error_no_template(const struct client_state *csp,
    1170             :                              struct http_response *rsp,
    1171             :                              const char *template_name)
    1172             : {
    1173             :    static const char status[] =
    1174             :       "500 Internal Privoxy Error";
    1175             :    static const char body_prefix[] =
    1176             :       "<html>\n"
    1177             :       "<head>\n"
    1178             :       " <title>500 Internal Privoxy Error</title>\n"
    1179             :       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
    1180             :       "</head>\n"
    1181             :       "<body>\n"
    1182             :       "<h1>500 Internal Privoxy Error</h1>\n"
    1183             :       "<p>Privoxy encountered an error while processing your request:</p>\n"
    1184             :       "<p><b>Could not load template file <code>";
    1185             :    static const char body_suffix[] =
    1186             :       "</code> or one of its included components.</b></p>\n"
    1187             :       "<p>Please contact your proxy administrator.</p>\n"
    1188             :       "<p>If you are the proxy administrator, please put the required file(s)"
    1189             :       "in the <code><i>(confdir)</i>/templates</code> directory.  The "
    1190             :       "location of the <code><i>(confdir)</i></code> directory "
    1191             :       "is specified in the main Privoxy <code>config</code> "
    1192             :       "file.  (It's typically the Privoxy install directory"
    1193             : #ifndef _WIN32
    1194             :       ", or <code>/etc/privoxy/</code>"
    1195             : #endif /* ndef _WIN32 */
    1196             :       ").</p>\n"
    1197             :       "</body>\n"
    1198             :       "</html>\n";
    1199         335 :    const size_t body_size = strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1;
    1200             : 
    1201         335 :    assert(csp);
    1202         335 :    assert(rsp);
    1203         335 :    assert(template_name);
    1204             : 
    1205             :    /* Reset rsp, if needed */
    1206         335 :    freez(rsp->status);
    1207         335 :    freez(rsp->head);
    1208         335 :    freez(rsp->body);
    1209         335 :    rsp->content_length = 0;
    1210         335 :    rsp->head_length = 0;
    1211         335 :    rsp->is_static = 0;
    1212             : 
    1213         335 :    rsp->body = malloc_or_die(body_size);
    1214         335 :    strlcpy(rsp->body, body_prefix, body_size);
    1215         335 :    strlcat(rsp->body, template_name, body_size);
    1216         335 :    strlcat(rsp->body, body_suffix, body_size);
    1217             : 
    1218         335 :    rsp->status = strdup(status);
    1219         335 :    if (rsp->status == NULL)
    1220             :    {
    1221           0 :       return JB_ERR_MEMORY;
    1222             :    }
    1223             : 
    1224         335 :    return JB_ERR_OK;
    1225             : }
    1226             : 
    1227             : 
    1228             : /*********************************************************************
    1229             :  *
    1230             :  * Function    :  cgi_error_unknown
    1231             :  *
    1232             :  * Description :  Almost-CGI function that is called if an unexpected
    1233             :  *                error occurs in the top-level CGI dispatcher.
    1234             :  *                In this context, "unexpected" means "anything other
    1235             :  *                than JB_ERR_MEMORY or JB_ERR_CGI_PARAMS" - CGIs are
    1236             :  *                expected to handle all other errors internally,
    1237             :  *                since they can give more relevant error messages
    1238             :  *                that way.
    1239             :  *
    1240             :  *                Note this is not a true CGI, it takes an error
    1241             :  *                code rather than a map of parameters.
    1242             :  *
    1243             :  * Parameters  :
    1244             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1245             :  *          2  :  rsp = http_response data structure for output
    1246             :  *          3  :  error_to_report = Error code to report.
    1247             :  *
    1248             :  * Returns     :  JB_ERR_OK on success
    1249             :  *                JB_ERR_MEMORY on out-of-memory error.
    1250             :  *
    1251             :  *********************************************************************/
    1252           0 : jb_err cgi_error_unknown(const struct client_state *csp,
    1253             :                          struct http_response *rsp,
    1254             :                          jb_err error_to_report)
    1255             : {
    1256             :    static const char status[] =
    1257             :       "500 Internal Privoxy Error";
    1258             :    static const char body_prefix[] =
    1259             :       "<html>\n"
    1260             :       "<head>\n"
    1261             :       " <title>500 Internal Privoxy Error</title>\n"
    1262             :       " <link rel=\"shortcut icon\" href=\"" CGI_PREFIX "error-favicon.ico\" type=\"image/x-icon\">"
    1263             :       "</head>\n"
    1264             :       "<body>\n"
    1265             :       "<h1>500 Internal Privoxy Error</h1>\n"
    1266             :       "<p>Privoxy encountered an error while processing your request:</p>\n"
    1267             :       "<p><b>Unexpected internal error: ";
    1268             :    static const char body_suffix[] =
    1269             :       "</b></p>\n"
    1270             :       "<p>Please "
    1271             :       "<a href=\"https://sourceforge.net/p/ijbswa/bugs/\">"
    1272             :       "file a bug report</a>.</p>\n"
    1273             :       "</body>\n"
    1274             :       "</html>\n";
    1275             :    /* Includes room for larger error numbers in the future. */
    1276           0 :    const size_t body_size = sizeof(body_prefix) + sizeof(body_suffix) + 5;
    1277           0 :    assert(csp);
    1278           0 :    assert(rsp);
    1279             : 
    1280             :    /* Reset rsp, if needed */
    1281           0 :    freez(rsp->status);
    1282           0 :    freez(rsp->head);
    1283           0 :    freez(rsp->body);
    1284           0 :    rsp->content_length = 0;
    1285           0 :    rsp->head_length = 0;
    1286           0 :    rsp->is_static = 0;
    1287           0 :    rsp->crunch_reason = INTERNAL_ERROR;
    1288             : 
    1289           0 :    rsp->body = malloc_or_die(body_size);
    1290             : 
    1291           0 :    snprintf(rsp->body, body_size, "%s%d%s", body_prefix, error_to_report, body_suffix);
    1292             : 
    1293           0 :    rsp->status = strdup(status);
    1294           0 :    if (rsp->status == NULL)
    1295             :    {
    1296           0 :       return JB_ERR_MEMORY;
    1297             :    }
    1298             : 
    1299           0 :    return JB_ERR_OK;
    1300             : }
    1301             : 
    1302             : 
    1303             : /*********************************************************************
    1304             :  *
    1305             :  * Function    :  cgi_error_bad_param
    1306             :  *
    1307             :  * Description :  CGI function that is called if the parameters
    1308             :  *                (query string) for a CGI were wrong.
    1309             :  *
    1310             :  * Parameters  :
    1311             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1312             :  *          2  :  rsp = http_response data structure for output
    1313             :  *
    1314             :  * CGI Parameters : none
    1315             :  *
    1316             :  * Returns     :  JB_ERR_OK on success
    1317             :  *                JB_ERR_MEMORY on out-of-memory error.
    1318             :  *
    1319             :  *********************************************************************/
    1320        1941 : jb_err cgi_error_bad_param(const struct client_state *csp,
    1321             :                            struct http_response *rsp)
    1322             : {
    1323             :    struct map *exports;
    1324             : 
    1325        1941 :    assert(csp);
    1326        1941 :    assert(rsp);
    1327             : 
    1328        1941 :    if (NULL == (exports = default_exports(csp, NULL)))
    1329             :    {
    1330           0 :       return JB_ERR_MEMORY;
    1331             :    }
    1332             : 
    1333        1941 :    return template_fill_for_cgi(csp, "cgi-error-bad-param", exports, rsp);
    1334             : }
    1335             : 
    1336             : 
    1337             : /*********************************************************************
    1338             :  *
    1339             :  * Function    :  cgi_redirect
    1340             :  *
    1341             :  * Description :  CGI support function to generate a HTTP redirect
    1342             :  *                message
    1343             :  *
    1344             :  * Parameters  :
    1345             :  *          1  :  rsp = http_response data structure for output
    1346             :  *          2  :  target = string with the target URL
    1347             :  *
    1348             :  * CGI Parameters : None
    1349             :  *
    1350             :  * Returns     :  JB_ERR_OK on success
    1351             :  *                JB_ERR_MEMORY on out-of-memory error.
    1352             :  *
    1353             :  *********************************************************************/
    1354        2316 : jb_err cgi_redirect (struct http_response * rsp, const char *target)
    1355             : {
    1356             :    jb_err err;
    1357             : 
    1358        2316 :    assert(rsp);
    1359        2316 :    assert(target);
    1360             : 
    1361        2316 :    err = enlist_unique_header(rsp->headers, "Location", target);
    1362             : 
    1363        2316 :    rsp->status = strdup("302 Local Redirect from Privoxy");
    1364        2316 :    if (rsp->status == NULL)
    1365             :    {
    1366           0 :       return JB_ERR_MEMORY;
    1367             :    }
    1368             : 
    1369        2316 :    return err;
    1370             : }
    1371             : 
    1372             : 
    1373             : /*********************************************************************
    1374             :  *
    1375             :  * Function    :  add_help_link
    1376             :  *
    1377             :  * Description :  Produce a copy of the string given as item,
    1378             :  *                embedded in an HTML link to its corresponding
    1379             :  *                section (item name in uppercase) in the actions
    1380             :  *                chapter of the user manual, (whose URL is given in
    1381             :  *                the config and defaults to our web site).
    1382             :  *
    1383             :  *                FIXME: I currently only work for actions, and would
    1384             :  *                       like to be generalized for other topics.
    1385             :  *
    1386             :  * Parameters  :
    1387             :  *          1  :  item = item (will NOT be free()d.)
    1388             :  *                       It is assumed to be HTML-safe.
    1389             :  *          2  :  config = The current configuration.
    1390             :  *
    1391             :  * Returns     :  String with item embedded in link, or NULL on
    1392             :  *                out-of-memory
    1393             :  *
    1394             :  *********************************************************************/
    1395      215272 : char *add_help_link(const char *item,
    1396             :                     struct configuration_spec *config)
    1397             : {
    1398             :    char *result;
    1399             : 
    1400      215272 :    if (!item) return NULL;
    1401             : 
    1402      215272 :    result = strdup("<a href=\"");
    1403      430544 :    if (!strncmpic(config->usermanual, "file://", 7) ||
    1404      215272 :        !strncmpic(config->usermanual, "http", 4))
    1405             :    {
    1406      215272 :       string_append(&result, config->usermanual);
    1407             :    }
    1408             :    else
    1409             :    {
    1410           0 :       string_append(&result, "http://");
    1411           0 :       string_append(&result, CGI_SITE_2_HOST);
    1412           0 :       string_append(&result, "/user-manual/");
    1413             :    }
    1414      215272 :    string_append(&result, ACTIONS_HELP_PREFIX);
    1415      215272 :    string_join  (&result, string_toupper(item));
    1416      215272 :    string_append(&result, "\">");
    1417      215272 :    string_append(&result, item);
    1418      215272 :    string_append(&result, "</a>");
    1419             : 
    1420      215272 :    return result;
    1421             : }
    1422             : 
    1423             : 
    1424             : /*********************************************************************
    1425             :  *
    1426             :  * Function    :  get_http_time
    1427             :  *
    1428             :  * Description :  Get the time in a format suitable for use in a
    1429             :  *                HTTP header - e.g.:
    1430             :  *                "Sun, 06 Nov 1994 08:49:37 GMT"
    1431             :  *
    1432             :  * Parameters  :
    1433             :  *          1  :  time_offset = Time returned will be current time
    1434             :  *                              plus this number of seconds.
    1435             :  *          2  :  buf = Destination for result.
    1436             :  *          3  :  buffer_size = Size of the buffer above. Must be big
    1437             :  *                              enough to hold 29 characters plus a
    1438             :  *                              trailing zero.
    1439             :  *
    1440             :  * Returns     :  N/A
    1441             :  *
    1442             :  *********************************************************************/
    1443       28889 : void get_http_time(int time_offset, char *buf, size_t buffer_size)
    1444             : {
    1445             :    struct tm *t;
    1446             :    time_t current_time;
    1447             : #if defined(HAVE_GMTIME_R)
    1448             :    struct tm dummy;
    1449             : #endif
    1450             : 
    1451       28889 :    assert(buf);
    1452       28889 :    assert(buffer_size > (size_t)29);
    1453             : 
    1454       28889 :    time(&current_time);
    1455             : 
    1456       28889 :    current_time += time_offset;
    1457             : 
    1458             :    /* get and save the gmt */
    1459             : #if HAVE_GMTIME_R
    1460       28889 :    t = gmtime_r(&current_time, &dummy);
    1461             : #elif defined(MUTEX_LOCKS_AVAILABLE)
    1462             :    privoxy_mutex_lock(&gmtime_mutex);
    1463             :    t = gmtime(&current_time);
    1464             :    privoxy_mutex_unlock(&gmtime_mutex);
    1465             : #else
    1466             :    t = gmtime(&current_time);
    1467             : #endif
    1468             : 
    1469       28889 :    strftime(buf, buffer_size, "%a, %d %b %Y %H:%M:%S GMT", t);
    1470             : 
    1471       28889 : }
    1472             : 
    1473             : /*********************************************************************
    1474             :  *
    1475             :  * Function    :  get_locale_time
    1476             :  *
    1477             :  * Description :  Get the time in a date(1)-like format
    1478             :  *                according to the current locale - e.g.:
    1479             :  *                "Fri Aug 29 19:37:12 CEST 2008"
    1480             :  *
    1481             :  *                XXX: Should we allow the user to change the format?
    1482             :  *
    1483             :  * Parameters  :
    1484             :  *          1  :  buf         = Destination for result.
    1485             :  *          2  :  buffer_size = Size of the buffer above. Must be big
    1486             :  *                              enough to hold 29 characters plus a
    1487             :  *                              trailing zero.
    1488             :  *
    1489             :  * Returns     :  N/A
    1490             :  *
    1491             :  *********************************************************************/
    1492       19338 : static void get_locale_time(char *buf, size_t buffer_size)
    1493             : {
    1494             :    struct tm *timeptr;
    1495             :    time_t current_time;
    1496             : #if defined(HAVE_LOCALTIME_R)
    1497             :    struct tm dummy;
    1498             : #endif
    1499             : 
    1500       19338 :    assert(buf);
    1501       19338 :    assert(buffer_size > (size_t)29);
    1502             : 
    1503       19338 :    time(&current_time);
    1504             : 
    1505             : #if HAVE_LOCALTIME_R
    1506       19338 :    timeptr = localtime_r(&current_time, &dummy);
    1507             : #elif defined(MUTEX_LOCKS_AVAILABLE)
    1508             :    privoxy_mutex_lock(&localtime_mutex);
    1509             :    timeptr = localtime(&current_time);
    1510             : #else
    1511             :    timeptr = localtime(&current_time);
    1512             : #endif
    1513             : 
    1514       19338 :    strftime(buf, buffer_size, "%a %b %d %X %Z %Y", timeptr);
    1515             : 
    1516             : #if !defined(HAVE_LOCALTIME_R) && defined(MUTEX_LOCKS_AVAILABLE)
    1517             :    privoxy_mutex_unlock(&localtime_mutex);
    1518             : #endif
    1519       19338 : }
    1520             : 
    1521             : 
    1522             : #ifdef FEATURE_COMPRESSION
    1523             : /*********************************************************************
    1524             :  *
    1525             :  * Function    :  compress_buffer
    1526             :  *
    1527             :  * Description :  Compresses the content of a buffer with zlib's deflate
    1528             :  *                Allocates a new buffer for the result, free'ing it is
    1529             :  *                up to the caller.
    1530             :  *
    1531             :  * Parameters  :
    1532             :  *          1  :  buffer = buffer whose content should be compressed
    1533             :  *          2  :  buffer_length = length of the buffer
    1534             :  *          3  :  compression_level = compression level for compress2()
    1535             :  *
    1536             :  * Returns     :  NULL on error, otherwise a pointer to the compressed
    1537             :  *                content of the input buffer.
    1538             :  *
    1539             :  *********************************************************************/
    1540         965 : char *compress_buffer(char *buffer, size_t *buffer_length, int compression_level)
    1541             : {
    1542             :    char *compressed_buffer;
    1543             :    uLongf new_length;
    1544         965 :    assert(-1 <= compression_level && compression_level <= 9);
    1545             : 
    1546             :    /* Let zlib figure out the maximum length of the compressed data */
    1547         965 :    new_length = compressBound((uLongf)*buffer_length);
    1548             : 
    1549         965 :    compressed_buffer = malloc_or_die(new_length);
    1550             : 
    1551         965 :    if (Z_OK != compress2((Bytef *)compressed_buffer, &new_length,
    1552             :          (Bytef *)buffer, *buffer_length, compression_level))
    1553             :    {
    1554           0 :       log_error(LOG_LEVEL_ERROR,
    1555             :          "compress2() failed. Buffer size: %lu, compression level: %d.",
    1556             :          new_length, compression_level);
    1557           0 :       freez(compressed_buffer);
    1558           0 :       return NULL;
    1559             :    }
    1560             : 
    1561         965 :    log_error(LOG_LEVEL_RE_FILTER,
    1562             :       "Compressed content from %lu to %lu bytes. Compression level: %d",
    1563             :       *buffer_length, new_length, compression_level);
    1564             : 
    1565         965 :    *buffer_length = (size_t)new_length;
    1566             : 
    1567         965 :    return compressed_buffer;
    1568             : 
    1569             : }
    1570             : #endif
    1571             : 
    1572             : 
    1573             : /*********************************************************************
    1574             :  *
    1575             :  * Function    :  finish_http_response
    1576             :  *
    1577             :  * Description :  Fill in the missing headers in an http response,
    1578             :  *                and flatten the headers to an http head.
    1579             :  *                For HEAD requests the body is freed once
    1580             :  *                the Content-Length header is set.
    1581             :  *
    1582             :  * Parameters  :
    1583             :  *          1  :  rsp = pointer to http_response to be processed
    1584             :  *
    1585             :  * Returns     :  A http_response, usually the rsp parameter.
    1586             :  *                On error, free()s rsp and returns cgi_error_memory()
    1587             :  *
    1588             :  *********************************************************************/
    1589       26436 : struct http_response *finish_http_response(struct client_state *csp, struct http_response *rsp)
    1590             : {
    1591             :    char buf[BUFFER_SIZE];
    1592             :    jb_err err;
    1593             : 
    1594             :    /* Special case - do NOT change this statically allocated response,
    1595             :     * which is ready for output anyway.
    1596             :     */
    1597       26436 :    if (rsp == cgi_error_memory_response)
    1598             :    {
    1599           0 :       return rsp;
    1600             :    }
    1601             : 
    1602             :    /*
    1603             :     * Add "Cross-origin resource sharing" (CORS) headers if enabled
    1604             :     */
    1605       26436 :    if (NULL != csp->config->cors_allowed_origin)
    1606             :    {
    1607       26436 :       enlist_unique_header(rsp->headers, "Access-Control-Allow-Origin",
    1608       26436 :          csp->config->cors_allowed_origin);
    1609       26436 :       enlist_unique_header(rsp->headers, "Access-Control-Allow-Methods", "GET,POST");
    1610       26436 :       enlist_unique_header(rsp->headers, "Access-Control-Allow-Headers", "X-Requested-With");
    1611       26436 :       enlist_unique_header(rsp->headers, "Access-Control-Max-Age", "86400");
    1612             :    }
    1613             : 
    1614             :    /*
    1615             :     * Fill in the HTTP Status, using HTTP/1.1
    1616             :     * unless the client asked for HTTP/1.0.
    1617             :     */
    1618       52872 :    snprintf(buf, sizeof(buf), "%s %s",
    1619       26436 :       strcmpic(csp->http->version, "HTTP/1.0") ? "HTTP/1.1" : "HTTP/1.0",
    1620       26436 :       rsp->status ? rsp->status : "200 OK");
    1621       26436 :    err = enlist_first(rsp->headers, buf);
    1622             : 
    1623             :    /*
    1624             :     * Set the Content-Length
    1625             :     */
    1626       26436 :    if (rsp->content_length == 0)
    1627             :    {
    1628       23872 :       rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
    1629             :    }
    1630             : 
    1631             : #ifdef FEATURE_COMPRESSION
    1632       26436 :    if (!err && (csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
    1633        1477 :       && (rsp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
    1634             :    {
    1635             :       char *compressed_content;
    1636             : 
    1637         953 :       compressed_content = compress_buffer(rsp->body, &rsp->content_length,
    1638         953 :          csp->config->compression_level);
    1639         953 :       if (NULL != compressed_content)
    1640             :       {
    1641         953 :          freez(rsp->body);
    1642         953 :          rsp->body = compressed_content;
    1643         953 :          err = enlist_unique_header(rsp->headers, "Content-Encoding", "deflate");
    1644             :       }
    1645             :    }
    1646             : #endif
    1647             : 
    1648       26436 :    if (!err)
    1649             :    {
    1650       26436 :       snprintf(buf, sizeof(buf), "Content-Length: %d", (int)rsp->content_length);
    1651             :       /*
    1652             :        * Signal serve() that the client will be able to figure out
    1653             :        * the end of the response without having to close the connection.
    1654             :        */
    1655       26436 :       csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET;
    1656       26436 :       err = enlist(rsp->headers, buf);
    1657             :    }
    1658             : 
    1659       26436 :    if (0 == strcmpic(csp->http->gpc, "head"))
    1660             :    {
    1661             :       /*
    1662             :        * The client only asked for the head. Dispose
    1663             :        * the body and log an offensive message.
    1664             :        *
    1665             :        * While it may seem to be a bit inefficient to
    1666             :        * prepare the body if it isn't needed, it's the
    1667             :        * only way to get the Content-Length right for
    1668             :        * dynamic pages. We could have disposed the body
    1669             :        * earlier, but not without duplicating the
    1670             :        * Content-Length setting code above.
    1671             :        */
    1672         312 :       log_error(LOG_LEVEL_CGI, "Preparing to give head to %s.", csp->ip_addr_str);
    1673         312 :       freez(rsp->body);
    1674         312 :       rsp->content_length = 0;
    1675             :    }
    1676             : 
    1677       26436 :    if (strncmpic(rsp->status, "302", 3))
    1678             :    {
    1679             :       /*
    1680             :        * If it's not a redirect without any content,
    1681             :        * set the Content-Type to text/html if it's
    1682             :        * not already specified.
    1683             :        */
    1684       22339 :       if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13);
    1685             :    }
    1686             : 
    1687             :    /*
    1688             :     * Fill in the rest of the default headers:
    1689             :     *
    1690             :     * Date: set to current date/time.
    1691             :     * Last-Modified: set to date/time the page was last changed.
    1692             :     * Expires: set to date/time page next needs reloading.
    1693             :     * Cache-Control: set to "no-cache" if applicable.
    1694             :     *
    1695             :     * See http://www.w3.org/Protocols/rfc2068/rfc2068
    1696             :     */
    1697       26436 :    if (rsp->is_static)
    1698             :    {
    1699             :       /*
    1700             :        * Set Expires to about 10 min into the future so it'll get reloaded
    1701             :        * occasionally, e.g. if Privoxy gets upgraded.
    1702             :        */
    1703             : 
    1704        2379 :       if (!err)
    1705             :       {
    1706        2379 :          get_http_time(0, buf, sizeof(buf));
    1707        2379 :          err = enlist_unique_header(rsp->headers, "Date", buf);
    1708             :       }
    1709             : 
    1710             :       /* Some date in the past. */
    1711        2379 :       if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT");
    1712             : 
    1713        2379 :       if (!err)
    1714             :       {
    1715        2379 :          get_http_time(10 * 60, buf, sizeof(buf)); /* 10 * 60sec = 10 minutes */
    1716        2379 :          err = enlist_unique_header(rsp->headers, "Expires", buf);
    1717             :       }
    1718             :    }
    1719       24057 :    else if (!strncmpic(rsp->status, "302", 3))
    1720             :    {
    1721        4097 :       get_http_time(0, buf, sizeof(buf));
    1722        4097 :       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
    1723             :    }
    1724             :    else
    1725             :    {
    1726             :       /*
    1727             :        * Setting "Cache-Control" to "no-cache" and  "Expires" to
    1728             :        * the current time doesn't exactly forbid caching, it just
    1729             :        * requires the client to revalidate the cached copy.
    1730             :        *
    1731             :        * If a temporary problem occurs and the user tries again after
    1732             :        * getting Privoxy's error message, a compliant browser may set the
    1733             :        * If-Modified-Since header with the content of the error page's
    1734             :        * Last-Modified header. More often than not, the document on the server
    1735             :        * is older than Privoxy's error message, the server would send status code
    1736             :        * 304 and the browser would display the outdated error message again and again.
    1737             :        *
    1738             :        * For documents delivered with status code 403, 404 and 503 we set "Last-Modified"
    1739             :        * to Tim Berners-Lee's birthday, which predates the age of any page on the web
    1740             :        * and can be safely used to "revalidate" without getting a status code 304.
    1741             :        *
    1742             :        * There is no need to let the useless If-Modified-Since header reach the
    1743             :        * server, it is therefore stripped by client_if_modified_since in parsers.c.
    1744             :        */
    1745       19960 :       if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
    1746             : 
    1747       19960 :       get_http_time(0, buf, sizeof(buf));
    1748       19960 :       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
    1749       19960 :       if (!strncmpic(rsp->status, "403", 3)
    1750       15826 :        || !strncmpic(rsp->status, "404", 3)
    1751       13718 :        || !strncmpic(rsp->status, "502", 3)
    1752       13651 :        || !strncmpic(rsp->status, "503", 3)
    1753       13610 :        || !strncmpic(rsp->status, "504", 3))
    1754             :       {
    1755        6350 :          if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Wed, 08 Jun 1955 12:00:00 GMT");
    1756             :       }
    1757             :       else
    1758             :       {
    1759       13610 :          if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf);
    1760             :       }
    1761       19960 :       if (!err) err = enlist_unique_header(rsp->headers, "Expires", "Sat, 17 Jun 2000 12:00:00 GMT");
    1762       19960 :       if (!err) err = enlist_unique_header(rsp->headers, "Pragma", "no-cache");
    1763             :    }
    1764             : 
    1765       26436 :    if (!err && (!(csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
    1766       26144 :               || (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)))
    1767             :    {
    1768         295 :       err = enlist_unique_header(rsp->headers, "Connection", "close");
    1769             :    }
    1770             : 
    1771             :    /*
    1772             :     * Write the head
    1773             :     */
    1774       26436 :    if (err || (NULL == (rsp->head = list_to_text(rsp->headers))))
    1775             :    {
    1776           0 :       free_http_response(rsp);
    1777           0 :       return cgi_error_memory();
    1778             :    }
    1779       26436 :    rsp->head_length = strlen(rsp->head);
    1780             : 
    1781       26436 :    return rsp;
    1782             : 
    1783             : }
    1784             : 
    1785             : 
    1786             : /*********************************************************************
    1787             :  *
    1788             :  * Function    :  alloc_http_response
    1789             :  *
    1790             :  * Description :  Allocates a new http_response structure.
    1791             :  *
    1792             :  * Parameters  :  N/A
    1793             :  *
    1794             :  * Returns     :  pointer to a new http_response, or NULL.
    1795             :  *
    1796             :  *********************************************************************/
    1797       26436 : struct http_response *alloc_http_response(void)
    1798             : {
    1799       26436 :    return (struct http_response *) zalloc(sizeof(struct http_response));
    1800             : 
    1801             : }
    1802             : 
    1803             : 
    1804             : /*********************************************************************
    1805             :  *
    1806             :  * Function    :  free_http_response
    1807             :  *
    1808             :  * Description :  Free the memory occupied by an http_response
    1809             :  *                and its depandant structures.
    1810             :  *
    1811             :  * Parameters  :
    1812             :  *          1  :  rsp = pointer to http_response to be freed
    1813             :  *
    1814             :  * Returns     :  N/A
    1815             :  *
    1816             :  *********************************************************************/
    1817       26436 : void free_http_response(struct http_response *rsp)
    1818             : {
    1819             :    /*
    1820             :     * Must special case cgi_error_memory_response, which is never freed.
    1821             :     */
    1822       26436 :    if (rsp && (rsp != cgi_error_memory_response))
    1823             :    {
    1824       26436 :       freez(rsp->status);
    1825       26436 :       freez(rsp->head);
    1826       26436 :       freez(rsp->body);
    1827       26436 :       destroy_list(rsp->headers);
    1828       26436 :       free(rsp);
    1829             :    }
    1830             : 
    1831       26436 : }
    1832             : 
    1833             : 
    1834             : /*********************************************************************
    1835             :  *
    1836             :  * Function    :  template_load
    1837             :  *
    1838             :  * Description :  CGI support function that loads a given HTML
    1839             :  *                template, ignoring comment lines and following
    1840             :  *                #include statements up to a depth of 1.
    1841             :  *
    1842             :  * Parameters  :
    1843             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    1844             :  *          2  :  template_ptr = Destination for pointer to loaded
    1845             :  *                               template text.
    1846             :  *          3  :  templatename = name of the HTML template to be used
    1847             :  *          4  :  recursive = Flag set if this function calls itself
    1848             :  *                            following an #include statament
    1849             :  *
    1850             :  * Returns     :  JB_ERR_OK on success
    1851             :  *                JB_ERR_MEMORY on out-of-memory error.
    1852             :  *                JB_ERR_FILE if the template file cannot be read
    1853             :  *
    1854             :  *********************************************************************/
    1855       94532 : jb_err template_load(const struct client_state *csp, char **template_ptr,
    1856             :                      const char *templatename, int recursive)
    1857             : {
    1858             :    jb_err err;
    1859             :    char *templates_dir_path;
    1860             :    char *full_path;
    1861             :    char *file_buffer;
    1862             :    char *included_module;
    1863             :    const char *p;
    1864             :    FILE *fp;
    1865             :    char buf[BUFFER_SIZE];
    1866             : 
    1867       94532 :    assert(csp);
    1868       94532 :    assert(template_ptr);
    1869       94532 :    assert(templatename);
    1870             : 
    1871       94532 :    *template_ptr = NULL;
    1872             : 
    1873             :    /* Validate template name.  Paranoia. */
    1874     1623038 :    for (p = templatename; *p != 0; p++)
    1875             :    {
    1876     1528506 :       if ( ((*p < 'a') || (*p > 'z'))
    1877      191097 :         && ((*p < 'A') || (*p > 'Z'))
    1878      191097 :         && ((*p < '0') || (*p > '9'))
    1879      184773 :         && (*p != '-')
    1880        1365 :         && (*p != '.'))
    1881             :       {
    1882             :          /* Illegal character */
    1883           0 :          return JB_ERR_FILE;
    1884             :       }
    1885             :    }
    1886             : 
    1887             :    /*
    1888             :     * Generate full path using either templdir
    1889             :     * or confdir/templates as base directory.
    1890             :     */
    1891       94532 :    if (NULL != csp->config->templdir)
    1892             :    {
    1893           0 :       templates_dir_path = strdup(csp->config->templdir);
    1894             :    }
    1895             :    else
    1896             :    {
    1897       94532 :       templates_dir_path = make_path(csp->config->confdir, "templates");
    1898             :    }
    1899             : 
    1900       94532 :    if (templates_dir_path == NULL)
    1901             :    {
    1902           0 :       log_error(LOG_LEVEL_ERROR, "Out of memory while generating template path for %s.",
    1903             :          templatename);
    1904           0 :       return JB_ERR_MEMORY;
    1905             :    }
    1906             : 
    1907       94532 :    full_path = make_path(templates_dir_path, templatename);
    1908       94532 :    free(templates_dir_path);
    1909       94532 :    if (full_path == NULL)
    1910             :    {
    1911           0 :       log_error(LOG_LEVEL_ERROR, "Out of memory while generating full template path for %s.",
    1912             :          templatename);
    1913           0 :       return JB_ERR_MEMORY;
    1914             :    }
    1915             : 
    1916             :    /* Allocate buffer */
    1917             : 
    1918       94532 :    file_buffer = strdup("");
    1919       94532 :    if (file_buffer == NULL)
    1920             :    {
    1921           0 :       log_error(LOG_LEVEL_ERROR, "Not enough free memory to buffer %s.", full_path);
    1922           0 :       free(full_path);
    1923           0 :       return JB_ERR_MEMORY;
    1924             :    }
    1925             : 
    1926             :    /* Open template file */
    1927             : 
    1928       94532 :    if (NULL == (fp = fopen(full_path, "r")))
    1929             :    {
    1930           0 :       log_error(LOG_LEVEL_ERROR, "Cannot open template file %s: %E", full_path);
    1931           0 :       free(full_path);
    1932           0 :       free(file_buffer);
    1933           0 :       return JB_ERR_FILE;
    1934             :    }
    1935       94532 :    free(full_path);
    1936             : 
    1937             :    /*
    1938             :     * Read the file, ignoring comments, and honoring #include
    1939             :     * statements, unless we're already called recursively.
    1940             :     *
    1941             :     * XXX: The comment handling could break with lines lengths > sizeof(buf).
    1942             :     *      This is unlikely in practise.
    1943             :     */
    1944     5146448 :    while (fgets(buf, sizeof(buf), fp))
    1945             :    {
    1946     5051916 :       if (!recursive && !strncmp(buf, "#include ", 9))
    1947             :       {
    1948       69950 :          if (JB_ERR_OK != (err = template_load(csp, &included_module, chomp(buf + 9), 1)))
    1949             :          {
    1950           0 :             free(file_buffer);
    1951           0 :             fclose(fp);
    1952           0 :             return err;
    1953             :          }
    1954             : 
    1955       69950 :          if (string_join(&file_buffer, included_module))
    1956             :          {
    1957           0 :             fclose(fp);
    1958           0 :             return JB_ERR_MEMORY;
    1959             :          }
    1960             : 
    1961       69950 :          continue;
    1962             :       }
    1963             : 
    1964             :       /* skip lines starting with '#' */
    1965     4981966 :       if (*buf == '#')
    1966             :       {
    1967     2029686 :          continue;
    1968             :       }
    1969             : 
    1970     2952280 :       if (string_append(&file_buffer, buf))
    1971             :       {
    1972           0 :          fclose(fp);
    1973           0 :          return JB_ERR_MEMORY;
    1974             :       }
    1975             :    }
    1976       94532 :    fclose(fp);
    1977             : 
    1978       94532 :    *template_ptr = file_buffer;
    1979             : 
    1980       94532 :    return JB_ERR_OK;
    1981             : }
    1982             : 
    1983             : 
    1984             : /*********************************************************************
    1985             :  *
    1986             :  * Function    :  template_fill
    1987             :  *
    1988             :  * Description :  CGI support function that fills in a pre-loaded
    1989             :  *                HTML template by replacing @name@ with value using
    1990             :  *                pcrs, for each item in the output map.
    1991             :  *
    1992             :  *                Note that a leading '$' character in the export map's
    1993             :  *                values will be stripped and toggle on backreference
    1994             :  *                interpretation.
    1995             :  *
    1996             :  * Parameters  :
    1997             :  *          1  :  template_ptr = IN: Template to be filled out.
    1998             :  *                                   Will be free()d.
    1999             :  *                               OUT: Filled out template.
    2000             :  *                                    Caller must free().
    2001             :  *          2  :  exports = map with fill in symbol -> name pairs
    2002             :  *
    2003             :  * Returns     :  JB_ERR_OK on success (and for uncritical errors)
    2004             :  *                JB_ERR_MEMORY on out-of-memory error
    2005             :  *
    2006             :  *********************************************************************/
    2007      117646 : jb_err template_fill(char **template_ptr, const struct map *exports)
    2008             : {
    2009             :    struct map_entry *m;
    2010             :    pcrs_job *job;
    2011             :    char buf[BUFFER_SIZE];
    2012             :    char *tmp_out_buffer;
    2013             :    char *file_buffer;
    2014             :    size_t size;
    2015             :    int error;
    2016             :    const char *flags;
    2017             : 
    2018      117646 :    assert(template_ptr);
    2019      117646 :    assert(*template_ptr);
    2020      117646 :    assert(exports);
    2021             : 
    2022      117646 :    file_buffer = *template_ptr;
    2023      117646 :    size = strlen(file_buffer) + 1;
    2024             : 
    2025             :    /*
    2026             :     * Assemble pcrs joblist from exports map
    2027             :     */
    2028     1266974 :    for (m = exports->first; m != NULL; m = m->next)
    2029             :    {
    2030     1149328 :       if (*m->name == '$')
    2031             :       {
    2032             :          /*
    2033             :           * First character of name is '$', so remove this flag
    2034             :           * character and allow backreferences ($1 etc) in the
    2035             :           * "replace with" text.
    2036             :           */
    2037           0 :          snprintf(buf, sizeof(buf), "%s", m->name + 1);
    2038           0 :          flags = "sigU";
    2039             :       }
    2040             :       else
    2041             :       {
    2042             :          /*
    2043             :           * Treat the "replace with" text as a literal string -
    2044             :           * no quoting needed, no backreferences allowed.
    2045             :           * ("Trivial" ['T'] flag).
    2046             :           */
    2047     1149328 :          flags = "sigTU";
    2048             : 
    2049             :          /* Enclose name in @@ */
    2050     1149328 :          snprintf(buf, sizeof(buf), "@%s@", m->name);
    2051             :       }
    2052             : 
    2053     1149328 :       log_error(LOG_LEVEL_CGI, "Substituting: s/%s/%s/%s", buf, m->value, flags);
    2054             : 
    2055             :       /* Make and run job. */
    2056     1149328 :       job = pcrs_compile(buf, m->value, flags,  &error);
    2057     1149328 :       if (job == NULL)
    2058             :       {
    2059           0 :          if (error == PCRS_ERR_NOMEM)
    2060             :          {
    2061           0 :             free(file_buffer);
    2062           0 :             *template_ptr = NULL;
    2063           0 :             return JB_ERR_MEMORY;
    2064             :          }
    2065             :          else
    2066             :          {
    2067           0 :             log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error);
    2068             :             /* Hope it wasn't important and silently ignore the invalid job */
    2069             :          }
    2070             :       }
    2071             :       else
    2072             :       {
    2073     1149328 :          error = pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size);
    2074             : 
    2075     1149328 :          pcrs_free_job(job);
    2076     1149328 :          if (NULL == tmp_out_buffer)
    2077             :          {
    2078           0 :             *template_ptr = NULL;
    2079           0 :             return JB_ERR_MEMORY;
    2080             :          }
    2081             : 
    2082     1149328 :          if (error < 0)
    2083             :          {
    2084             :             /*
    2085             :              * Substitution failed, keep the original buffer,
    2086             :              * log the problem and ignore it.
    2087             :              *
    2088             :              * The user might see some unresolved @CGI_VARIABLES@,
    2089             :              * but returning a special CGI error page seems unreasonable
    2090             :              * and could mask more important error messages.
    2091             :              */
    2092           0 :             free(tmp_out_buffer);
    2093           0 :             log_error(LOG_LEVEL_ERROR, "Failed to execute s/%s/%s/%s. %s",
    2094             :                buf, m->value, flags, pcrs_strerror(error));
    2095             :          }
    2096             :          else
    2097             :          {
    2098             :             /* Substitution succeeded, use modified buffer. */
    2099     1149328 :             free(file_buffer);
    2100     1149328 :             file_buffer = tmp_out_buffer;
    2101             :          }
    2102             :       }
    2103             :    }
    2104             : 
    2105             :    /*
    2106             :     * Return
    2107             :     */
    2108      117646 :    *template_ptr = file_buffer;
    2109      117646 :    return JB_ERR_OK;
    2110             : }
    2111             : 
    2112             : 
    2113             : /*********************************************************************
    2114             :  *
    2115             :  * Function    :  template_fill_for_cgi
    2116             :  *
    2117             :  * Description :  CGI support function that loads a HTML template
    2118             :  *                and fills it in.  Handles file-not-found errors
    2119             :  *                by sending a HTML error message.  For convenience,
    2120             :  *                this function also frees the passed "exports" map.
    2121             :  *
    2122             :  * Parameters  :
    2123             :  *          1  :  csp = Client state
    2124             :  *          2  :  templatename = name of the HTML template to be used
    2125             :  *          3  :  exports = map with fill in symbol -> name pairs.
    2126             :  *                          Will be freed by this function.
    2127             :  *          4  :  rsp = Response structure to fill in.
    2128             :  *
    2129             :  * Returns     :  JB_ERR_OK on success
    2130             :  *                JB_ERR_MEMORY on out-of-memory error
    2131             :  *
    2132             :  *********************************************************************/
    2133       18391 : jb_err template_fill_for_cgi(const struct client_state *csp,
    2134             :                              const char *templatename,
    2135             :                              struct map *exports,
    2136             :                              struct http_response *rsp)
    2137             : {
    2138             :    jb_err err;
    2139             : 
    2140       18391 :    assert(csp);
    2141       18391 :    assert(templatename);
    2142       18391 :    assert(exports);
    2143       18391 :    assert(rsp);
    2144             : 
    2145       18391 :    err = template_load(csp, &rsp->body, templatename, 0);
    2146       18391 :    if (err == JB_ERR_FILE)
    2147             :    {
    2148           0 :       err = cgi_error_no_template(csp, rsp, templatename);
    2149             :    }
    2150       18391 :    else if (err == JB_ERR_OK)
    2151             :    {
    2152       18391 :       err = template_fill(&rsp->body, exports);
    2153             :    }
    2154       18391 :    free_map(exports);
    2155       18391 :    return err;
    2156             : }
    2157             : 
    2158             : 
    2159             : /*********************************************************************
    2160             :  *
    2161             :  * Function    :  default_exports
    2162             :  *
    2163             :  * Description :  returns a struct map list that contains exports
    2164             :  *                which are common to all CGI functions.
    2165             :  *
    2166             :  * Parameters  :
    2167             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    2168             :  *          2  :  caller = name of CGI who calls us and which should
    2169             :  *                         be excluded from the generated menu. May be
    2170             :  *                         NULL.
    2171             :  * Returns     :  NULL if no memory, else a new map.  Caller frees.
    2172             :  *
    2173             :  *********************************************************************/
    2174       19338 : struct map *default_exports(const struct client_state *csp, const char *caller)
    2175             : {
    2176             :    char buf[30];
    2177             :    jb_err err;
    2178             :    struct map * exports;
    2179       19338 :    int local_help_exists = 0;
    2180       19338 :    char *ip_address = NULL;
    2181       19338 :    char *port = NULL;
    2182       19338 :    char *hostname = NULL;
    2183             : 
    2184       19338 :    assert(csp);
    2185             : 
    2186       19338 :    exports = new_map();
    2187             : 
    2188       19338 :    if (csp->config->hostname)
    2189             :    {
    2190           0 :       get_host_information(csp->cfd, &ip_address, &port, NULL);
    2191           0 :       hostname = strdup(csp->config->hostname);
    2192             :    }
    2193             :    else
    2194             :    {
    2195       19338 :       get_host_information(csp->cfd, &ip_address, &port, &hostname);
    2196             :    }
    2197             : 
    2198       19338 :    err = map(exports, "version", 1, html_encode(VERSION), 0);
    2199       19338 :    get_locale_time(buf, sizeof(buf));
    2200       19338 :    if (!err) err = map(exports, "time",          1, html_encode(buf), 0);
    2201       19338 :    if (!err) err = map(exports, "my-ip-address", 1, html_encode(ip_address ? ip_address : "unknown"), 0);
    2202       19338 :    freez(ip_address);
    2203       19338 :    if (!err) err = map(exports, "my-port",       1, html_encode(port ? port : "unknown"), 0);
    2204       19338 :    freez(port);
    2205       19338 :    if (!err) err = map(exports, "my-hostname",   1, html_encode(hostname ? hostname : "unknown"), 0);
    2206       19338 :    freez(hostname);
    2207       19338 :    if (!err) err = map(exports, "homepage",      1, html_encode(HOME_PAGE_URL), 0);
    2208       19338 :    if (!err)
    2209             :    {
    2210       19338 :       err = map(exports, "default-cgi",   1, html_encode(CGI_PREFIX), 0);
    2211             :    }
    2212       19338 :    if (!err) err = map(exports, "menu",          1, make_menu(csp, caller), 0);
    2213       19338 :    if (!err) err = map(exports, "code-status",   1, CODE_STATUS, 1);
    2214       38676 :    if (!strncmpic(csp->config->usermanual, "file://", 7) ||
    2215       19338 :        !strncmpic(csp->config->usermanual, "http", 4))
    2216             :    {
    2217             :       /* Manual is located somewhere else, just link to it. */
    2218       19338 :       if (!err) err = map(exports, "user-manual", 1, html_encode(csp->config->usermanual), 0);
    2219             :    }
    2220             :    else
    2221             :    {
    2222             :       /* Manual is delivered by Privoxy. */
    2223           0 :       if (!err)
    2224             :       {
    2225           0 :          err = map(exports, "user-manual", 1, html_encode(CGI_PREFIX"user-manual/"), 0);
    2226             :       }
    2227             :    }
    2228       19338 :    if (!err) err = map(exports, "actions-help-prefix", 1, ACTIONS_HELP_PREFIX ,1);
    2229             : #ifdef FEATURE_TOGGLE
    2230       19338 :    if (!err) err = map_conditional(exports, "enabled-display", global_toggle_state);
    2231             : #else
    2232             :    if (!err) err = map_block_killer(exports, "can-toggle");
    2233             : #endif
    2234             : 
    2235             :    if (!strcmp(CODE_STATUS, "stable"))
    2236             :    {
    2237       19338 :       if (!err) err = map_block_killer(exports, "unstable");
    2238             :    }
    2239             : 
    2240       19338 :    if (csp->config->admin_address != NULL)
    2241             :    {
    2242           0 :       if (!err) err = map(exports, "admin-address", 1, html_encode(csp->config->admin_address), 0);
    2243           0 :       local_help_exists = 1;
    2244             :    }
    2245             :    else
    2246             :    {
    2247       19338 :       if (!err) err = map_block_killer(exports, "have-adminaddr-info");
    2248             :    }
    2249             : 
    2250       19338 :    if (csp->config->proxy_info_url != NULL)
    2251             :    {
    2252           0 :       if (!err) err = map(exports, "proxy-info-url", 1, html_encode(csp->config->proxy_info_url), 0);
    2253           0 :       local_help_exists = 1;
    2254             :    }
    2255             :    else
    2256             :    {
    2257       19338 :       if (!err) err = map_block_killer(exports, "have-proxy-info");
    2258             :    }
    2259             : 
    2260       19338 :    if (local_help_exists == 0)
    2261             :    {
    2262       19338 :       if (!err) err = map_block_killer(exports, "have-help-info");
    2263             :    }
    2264             : 
    2265       19338 :    if (err)
    2266             :    {
    2267           0 :       free_map(exports);
    2268           0 :       return NULL;
    2269             :    }
    2270             : 
    2271       19338 :    return exports;
    2272             : }
    2273             : 
    2274             : 
    2275             : /*********************************************************************
    2276             :  *
    2277             :  * Function    :  map_block_killer
    2278             :  *
    2279             :  * Description :  Convenience function.
    2280             :  *                Adds a "killer" for the conditional HTML-template
    2281             :  *                block <name>, i.e. a substitution of the regex
    2282             :  *                "if-<name>-start.*if-<name>-end" to the given
    2283             :  *                export list.
    2284             :  *
    2285             :  * Parameters  :
    2286             :  *          1  :  exports = map to extend
    2287             :  *          2  :  name = name of conditional block
    2288             :  *
    2289             :  * Returns     :  JB_ERR_OK on success
    2290             :  *                JB_ERR_MEMORY on out-of-memory error.
    2291             :  *
    2292             :  *********************************************************************/
    2293      122717 : jb_err map_block_killer(struct map *exports, const char *name)
    2294             : {
    2295             :    char buf[1000]; /* Will do, since the names are hardwired */
    2296             : 
    2297      122717 :    assert(exports);
    2298      122717 :    assert(name);
    2299      122717 :    assert(strlen(name) < (size_t)490);
    2300             : 
    2301      122717 :    snprintf(buf, sizeof(buf), "if-%s-start.*if-%s-end", name, name);
    2302      245434 :    return map(exports, buf, 1, "", 1);
    2303             : }
    2304             : 
    2305             : 
    2306             : /*********************************************************************
    2307             :  *
    2308             :  * Function    :  map_block_keep
    2309             :  *
    2310             :  * Description :  Convenience function.  Removes the markers used
    2311             :  *                by map-block-killer, to save a few bytes.
    2312             :  *                i.e. removes "@if-<name>-start@" and "@if-<name>-end@"
    2313             :  *
    2314             :  * Parameters  :
    2315             :  *          1  :  exports = map to extend
    2316             :  *          2  :  name = name of conditional block
    2317             :  *
    2318             :  * Returns     :  JB_ERR_OK on success
    2319             :  *                JB_ERR_MEMORY on out-of-memory error.
    2320             :  *
    2321             :  *********************************************************************/
    2322       54520 : jb_err map_block_keep(struct map *exports, const char *name)
    2323             : {
    2324             :    jb_err err;
    2325             :    char buf[500]; /* Will do, since the names are hardwired */
    2326             : 
    2327       54520 :    assert(exports);
    2328       54520 :    assert(name);
    2329       54520 :    assert(strlen(name) < (size_t)490);
    2330             : 
    2331       54520 :    snprintf(buf, sizeof(buf), "if-%s-start", name);
    2332       54520 :    err = map(exports, buf, 1, "", 1);
    2333             : 
    2334       54520 :    if (err)
    2335             :    {
    2336           0 :       return err;
    2337             :    }
    2338             : 
    2339       54520 :    snprintf(buf, sizeof(buf), "if-%s-end", name);
    2340       54520 :    return map(exports, buf, 1, "", 1);
    2341             : }
    2342             : 
    2343             : 
    2344             : /*********************************************************************
    2345             :  *
    2346             :  * Function    :  map_conditional
    2347             :  *
    2348             :  * Description :  Convenience function.
    2349             :  *                Adds an "if-then-else" for the conditional HTML-template
    2350             :  *                block <name>, i.e. a substitution of the form:
    2351             :  *                @if-<name>-then@
    2352             :  *                   True text
    2353             :  *                @else-not-<name>@
    2354             :  *                   False text
    2355             :  *                @endif-<name>@
    2356             :  *
    2357             :  *                The control structure and one of the alternatives
    2358             :  *                will be hidden.
    2359             :  *
    2360             :  * Parameters  :
    2361             :  *          1  :  exports = map to extend
    2362             :  *          2  :  name = name of conditional block
    2363             :  *          3  :  choose_first = nonzero for first, zero for second.
    2364             :  *
    2365             :  * Returns     :  JB_ERR_OK on success
    2366             :  *                JB_ERR_MEMORY on out-of-memory error.
    2367             :  *
    2368             :  *********************************************************************/
    2369       37561 : jb_err map_conditional(struct map *exports, const char *name, int choose_first)
    2370             : {
    2371             :    char buf[1000]; /* Will do, since the names are hardwired */
    2372             :    jb_err err;
    2373             : 
    2374       37561 :    assert(exports);
    2375       37561 :    assert(name);
    2376       37561 :    assert(strlen(name) < (size_t)480);
    2377             : 
    2378       37561 :    snprintf(buf, sizeof(buf), (choose_first
    2379             :       ? "else-not-%s@.*@endif-%s"
    2380             :       : "if-%s-then@.*@else-not-%s"),
    2381             :       name, name);
    2382             : 
    2383       37561 :    err = map(exports, buf, 1, "", 1);
    2384       37561 :    if (err)
    2385             :    {
    2386           0 :       return err;
    2387             :    }
    2388             : 
    2389       37561 :    snprintf(buf, sizeof(buf), (choose_first ? "if-%s-then" : "endif-%s"), name);
    2390       37561 :    return map(exports, buf, 1, "", 1);
    2391             : }
    2392             : 
    2393             : 
    2394             : /*********************************************************************
    2395             :  *
    2396             :  * Function    :  make_menu
    2397             :  *
    2398             :  * Description :  Returns an HTML-formatted menu of the available
    2399             :  *                unhidden CGIs, excluding the one given in <self>
    2400             :  *                and the toggle CGI if toggling is disabled.
    2401             :  *
    2402             :  * Parameters  :
    2403             :  *          1  :  csp = Current client state (buffers, headers, etc...)
    2404             :  *          2  :  self = name of CGI to leave out, can be NULL for
    2405             :  *                complete listing.
    2406             :  *
    2407             :  * Returns     :  menu string, or NULL on out-of-memory error.
    2408             :  *
    2409             :  *********************************************************************/
    2410       19338 : char *make_menu(const struct client_state *csp, const char *self)
    2411             : {
    2412             :    const struct cgi_dispatcher *d;
    2413       19338 :    char *result = strdup("");
    2414             : 
    2415       19338 :    if (self == NULL)
    2416             :    {
    2417       10156 :       self = "NO-SUCH-CGI!";
    2418             :    }
    2419             : 
    2420             :    /* List available unhidden CGI's and export as "other-cgis" */
    2421      734844 :    for (d = cgi_dispatchers; d->name; d++)
    2422             :    {
    2423             : 
    2424             : #ifdef FEATURE_TOGGLE
    2425      715506 :       if (!(csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE) && !strcmp(d->name, "toggle"))
    2426             :       {
    2427             :          /*
    2428             :           * Suppress the toggle link if remote toggling is disabled.
    2429             :           */
    2430           0 :          continue;
    2431             :       }
    2432             : #endif /* def FEATURE_TOGGLE */
    2433             : 
    2434      715506 :       if (d->description && strcmp(d->name, self))
    2435             :       {
    2436             :          char *html_encoded_prefix;
    2437             : 
    2438             :          /*
    2439             :           * Line breaks would be great, but break
    2440             :           * the "blocked" template's JavaScript.
    2441             :           */
    2442      108671 :          string_append(&result, "<li><a href=\"");
    2443      108671 :          html_encoded_prefix = html_encode(CGI_PREFIX);
    2444      108671 :          if (html_encoded_prefix == NULL)
    2445             :          {
    2446           0 :             return NULL;
    2447             :          }
    2448             :          else
    2449             :          {
    2450      108671 :             string_append(&result, html_encoded_prefix);
    2451      108671 :             free(html_encoded_prefix);
    2452             :          }
    2453      108671 :          string_append(&result, d->name);
    2454      108671 :          string_append(&result, "\">");
    2455      108671 :          string_append(&result, d->description);
    2456      108671 :          string_append(&result, "</a></li>");
    2457             :       }
    2458             :    }
    2459             : 
    2460       19338 :    return result;
    2461             : }
    2462             : 
    2463             : 
    2464             : /*********************************************************************
    2465             :  *
    2466             :  * Function    :  dump_map
    2467             :  *
    2468             :  * Description :  HTML-dump a map for debugging (as table)
    2469             :  *
    2470             :  * Parameters  :
    2471             :  *          1  :  the_map = map to dump
    2472             :  *
    2473             :  * Returns     :  string with HTML
    2474             :  *
    2475             :  *********************************************************************/
    2476           0 : char *dump_map(const struct map *the_map)
    2477             : {
    2478             :    struct map_entry *cur_entry;
    2479           0 :    char *ret = strdup("");
    2480             : 
    2481           0 :    string_append(&ret, "<table>\n");
    2482             : 
    2483           0 :    for (cur_entry = the_map->first;
    2484           0 :         (cur_entry != NULL) && (ret != NULL);
    2485           0 :         cur_entry = cur_entry->next)
    2486             :    {
    2487           0 :       string_append(&ret, "<tr><td><b>");
    2488           0 :       string_join  (&ret, html_encode(cur_entry->name));
    2489           0 :       string_append(&ret, "</b></td><td>");
    2490           0 :       string_join  (&ret, html_encode(cur_entry->value));
    2491           0 :       string_append(&ret, "</td></tr>\n");
    2492             :    }
    2493             : 
    2494           0 :    string_append(&ret, "</table>\n");
    2495           0 :    return ret;
    2496             : }
    2497             : 
    2498             : 
    2499             : /*
    2500             :   Local Variables:
    2501             :   tab-width: 3
    2502             :   end:
    2503             : */

Generated by: LCOV version 1.14