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 & 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'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(¤t_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(¤t_time, &dummy);
1461 : #elif defined(MUTEX_LOCKS_AVAILABLE)
1462 : privoxy_mutex_lock(&gmtime_mutex);
1463 : t = gmtime(¤t_time);
1464 : privoxy_mutex_unlock(&gmtime_mutex);
1465 : #else
1466 : t = gmtime(¤t_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(¤t_time);
1504 :
1505 : #if HAVE_LOCALTIME_R
1506 19338 : timeptr = localtime_r(¤t_time, &dummy);
1507 : #elif defined(MUTEX_LOCKS_AVAILABLE)
1508 : privoxy_mutex_lock(&localtime_mutex);
1509 : timeptr = localtime(¤t_time);
1510 : #else
1511 : timeptr = localtime(¤t_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 : */
|