Catch ups.
[privoxy.git] / cgi.c
1 const char cgi_rcs[] = "$Id: cgi.c,v 1.48 2002/03/08 17:47:07 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/cgi.c,v $
5  *
6  * Purpose     :  Declares functions to intercept request, generate
7  *                html or gif answers, and to compose HTTP resonses.
8  *                This only contains the framework functions, the
9  *                actual handler functions are declared elsewhere.
10  *                
11  *                Functions declared include:
12  * 
13  *
14  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
15  *                IJBSWA team.  http://ijbswa.sourceforge.net
16  *
17  *                Based on the Internet Junkbuster originally written
18  *                by and Copyright (C) 1997 Anonymous Coders and 
19  *                Junkbusters Corporation.  http://www.junkbusters.com
20  *
21  *                This program is free software; you can redistribute it 
22  *                and/or modify it under the terms of the GNU General
23  *                Public License as published by the Free Software
24  *                Foundation; either version 2 of the License, or (at
25  *                your option) any later version.
26  *
27  *                This program is distributed in the hope that it will
28  *                be useful, but WITHOUT ANY WARRANTY; without even the
29  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
30  *                PARTICULAR PURPOSE.  See the GNU General Public
31  *                License for more details.
32  *
33  *                The GNU General Public License should be included with
34  *                this file.  If not, you can view it at
35  *                http://www.gnu.org/copyleft/gpl.html
36  *                or write to the Free Software Foundation, Inc., 59
37  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
38  *
39  * Revisions   :
40  *    $Log: cgi.c,v $
41  *    Revision 1.48  2002/03/08 17:47:07  jongfoster
42  *    Adding comments
43  *
44  *    Revision 1.47  2002/03/08 16:41:33  oes
45  *    Added GIF images again
46  *
47  *    Revision 1.46  2002/03/07 03:48:38  oes
48  *     - Changed built-in images from GIF to PNG
49  *       (with regard to Unisys patent issue)
50  *     - Added a 4x4 pattern PNG which is less intrusive
51  *       than the logo but also clearly marks the deleted banners
52  *
53  *    Revision 1.45  2002/03/06 22:54:35  jongfoster
54  *    Automated function-comment nitpicking.
55  *
56  *    Revision 1.44  2002/03/05 22:43:45  david__schmidt
57  *    - Better error reporting on OS/2
58  *    - Fix double-slash comment (oops)
59  *
60  *    Revision 1.43  2002/03/05 21:33:45  david__schmidt
61  *    - Re-enable OS/2 building after new parms were added
62  *    - Fix false out of memory report when resolving CGI templates when no IP
63  *      address is available of failed attempt (a la no such domain)
64  *
65  *    Revision 1.42  2002/01/21 00:33:20  jongfoster
66  *    Replacing strsav() with the safer string_append() or string_join().
67  *    Adding map_block_keep() to save a few bytes in the edit-actions-list HTML.
68  *    Adding missing html_encode() to error message generators.
69  *    Adding edit-actions-section-swap and many "shortcuts" to the list of CGIs.
70  *
71  *    Revision 1.41  2002/01/17 20:56:22  jongfoster
72  *    Replacing hard references to the URL of the config interface
73  *    with #defines from project.h
74  *
75  *    Revision 1.40  2002/01/09 14:26:46  oes
76  *    Added support for thread-safe gmtime_r call.
77  *
78  *    Revision 1.39  2001/11/16 00:48:13  jongfoster
79  *    Fixing a compiler warning
80  *
81  *    Revision 1.38  2001/11/13 00:31:21  jongfoster
82  *    - Adding new CGIs for use by non-JavaScript browsers:
83  *        edit-actions-url-form
84  *        edit-actions-add-url-form
85  *        edit-actions-remove-url-form
86  *    - Fixing make_menu()'s HTML generation - it now quotes the href parameter.
87  *    - Fixing || bug.
88  *
89  *    Revision 1.37  2001/11/01 14:28:47  david__schmidt
90  *    Show enablement/disablement status in almost all templates.
91  *    There is a little trickiness here: apparent recursive resolution of
92  *    @if-enabled-then@ caused the toggle template to show status out-of-phase with
93  *    the actual enablement status.  So a similar construct,
94  *    @if-enabled-display-then@, is used to resolve the status display on non-'toggle'
95  *    templates.
96  *
97  *    Revision 1.36  2001/10/26 17:33:27  oes
98  *    marginal bugfix
99  *
100  *    Revision 1.35  2001/10/23 21:48:19  jongfoster
101  *    Cleaning up error handling in CGI functions - they now send back
102  *    a HTML error page and should never cause a FATAL error.  (Fixes one
103  *    potential source of "denial of service" attacks).
104  *
105  *    CGI actions file editor that works and is actually useful.
106  *
107  *    Ability to toggle JunkBuster remotely using a CGI call.
108  *
109  *    You can turn off both the above features in the main configuration
110  *    file, e.g. if you are running a multi-user proxy.
111  *
112  *    Revision 1.34  2001/10/18 22:22:09  david__schmidt
113  *    Only show "Local support" on templates conditionally:
114  *      - if either 'admin-address' or 'proxy-info-url' are uncommented in config
115  *      - if not, no Local support section appears
116  *
117  *    Revision 1.33  2001/10/14 22:28:41  jongfoster
118  *    Fixing stupid typo.
119  *
120  *    Revision 1.32  2001/10/14 22:20:18  jongfoster
121  *    - Changes to CGI dispatching method to match CGI names exactly,
122  *      rather than doing a prefix match.
123  *    - No longer need to count the length of the CGI handler names by hand.
124  *    - Adding new handler for 404 error when disptching a CGI, if none of
125  *      the handlers match.
126  *    - Adding new handlers for CGI actionsfile editor.
127  *
128  *    Revision 1.31  2001/10/10 10:56:39  oes
129  *    Failiure to load template now fatal. Before, the user got a hard-to-understand assertion failure from cgi.c
130  *
131  *    Revision 1.30  2001/10/02 15:30:57  oes
132  *    Introduced show-request cgi
133  *
134  *    Revision 1.29  2001/09/20 15:47:44  steudten
135  *
136  *    Fix BUG: Modify int size to size_t size in fill_template()
137  *     - removes big trouble on machines where sizeof(int) != sizeof(size_t).
138  *
139  *    Revision 1.28  2001/09/19 18:00:37  oes
140  *     - Deletef time() FIXME (Can't fail under Linux either, if
141  *       the argument is guaranteed to be in out address space,
142  *       which it is.)
143  *     - Fixed comments
144  *     - Pointer notation cosmetics
145  *     - Fixed a minor bug in template_fill(): Failiure of
146  *       pcrs_execute() now secure.
147  *
148  *    Revision 1.27  2001/09/16 17:08:54  jongfoster
149  *    Moving simple CGI functions from cgi.c to new file cgisimple.c
150  *
151  *    Revision 1.26  2001/09/16 15:47:37  jongfoster
152  *    First version of CGI-based edit interface.  This is very much a
153  *    work-in-progress, and you can't actually use it to edit anything
154  *    yet.  You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
155  *    to have any effect.
156  *
157  *    Revision 1.25  2001/09/16 15:02:35  jongfoster
158  *    Adding i.j.b/robots.txt.
159  *    Inlining add_stats() since it's only ever called from one place.
160  *
161  *    Revision 1.24  2001/09/16 11:38:01  jongfoster
162  *    Splitting fill_template() into 2 functions:
163  *    template_load() loads the file
164  *    template_fill() performs the PCRS regexps.
165  *    This is because the CGI edit interface has a "table row"
166  *    template which is used many times in the page - this
167  *    change means it's only loaded from disk once.
168  *
169  *    Revision 1.23  2001/09/16 11:16:05  jongfoster
170  *    Better error handling in dispatch_cgi() and parse_cgi_parameters()
171  *
172  *    Revision 1.22  2001/09/16 11:00:10  jongfoster
173  *    New function alloc_http_response, for symmetry with free_http_response
174  *
175  *    Revision 1.21  2001/09/13 23:53:03  jongfoster
176  *    Support for both static and dynamically generated CGI pages.
177  *    Correctly setting Last-Modified: and Expires: HTTP headers.
178  *
179  *    Revision 1.20  2001/09/13 23:40:36  jongfoster
180  *    (Cosmetic only) Indentation correction
181  *
182  *    Revision 1.19  2001/09/13 23:31:25  jongfoster
183  *    Moving image data to cgi.c rather than cgi.h.
184  *
185  *    Revision 1.18  2001/08/05 16:06:20  jongfoster
186  *    Modifiying "struct map" so that there are now separate header and
187  *    "map_entry" structures.  This means that functions which modify a
188  *    map no longer need to return a pointer to the modified map.
189  *    Also, it no longer reverses the order of the entries (which may be
190  *    important with some advanced template substitutions).
191  *
192  *    Revision 1.17  2001/08/05 15:57:38  oes
193  *    Adapted finish_http_response to new list_to_text
194  *
195  *    Revision 1.16  2001/08/01 21:33:18  jongfoster
196  *    Changes to fill_template() that reduce memory usage without having
197  *    an impact on performance.  I also renamed some variables so as not
198  *    to clash with the C++ keywords "new" and "template".
199  *
200  *    Revision 1.15  2001/08/01 21:19:22  jongfoster
201  *    Moving file version information to a separate CGI page.
202  *
203  *    Revision 1.14  2001/08/01 00:19:03  jongfoster
204  *    New function: map_conditional() for an if-then-else syntax.
205  *    Changing to use new version of show_defines()
206  *
207  *    Revision 1.13  2001/07/30 22:08:36  jongfoster
208  *    Tidying up #defines:
209  *    - All feature #defines are now of the form FEATURE_xxx
210  *    - Permanently turned off WIN_GUI_EDIT
211  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
212  *
213  *    Revision 1.12  2001/07/29 18:47:05  jongfoster
214  *    Adding missing #include "loadcfg.h"
215  *
216  *    Revision 1.11  2001/07/18 17:24:37  oes
217  *    Changed to conform to new pcrs interface
218  *
219  *    Revision 1.10  2001/07/13 13:53:13  oes
220  *    Removed all #ifdef PCRS and related code
221  *
222  *    Revision 1.9  2001/06/29 21:45:41  oes
223  *    Indentation, CRLF->LF, Tab-> Space
224  *
225  *    Revision 1.8  2001/06/29 13:21:46  oes
226  *    - Cosmetics: renamed and reordered functions, variables,
227  *      texts, improved comments  etc
228  *
229  *    - Removed ij_untrusted_url() The relevant
230  *      info is now part of the "untrusted" page,
231  *      which is generated by filters.c:trust_url()
232  *
233  *    - Generators of content now call finish_http_response()
234  *      themselves, making jcc.c:chat() a little less
235  *      cluttered
236  *
237  *    - Removed obsolete "Pragma: no-cache" from our headers
238  *
239  *    - http_responses now know their head length
240  *
241  *    - fill_template now uses the new interface to pcrs, so that
242  *       - long jobs (like whole files) no longer have to be assembled
243  *         in a fixed size buffer
244  *       - the new T (trivial) option is used, and the replacement may
245  *         contain Perl syntax backrefs without confusing pcrs
246  *
247  *    - Introduced default_exports() which generates a set of exports
248  *      common to all CGIs and other content generators
249  *
250  *    - Introduced convenience function map_block_killer()
251  *
252  *    - Introduced convenience function make_menu()
253  *
254  *    - Introduced CGI-like function error_response() which generates
255  *      the "No such domain" and "Connect failed" messages using the
256  *      CGI platform
257  *
258  *    - cgi_show_url_info:
259  *      - adapted to new CGI features
260  *      - form and answers now generated from same template
261  *      - http:// prefix in URL now OK
262  *
263  *    - cgi_show_status:
264  *      - adapted to new CGI features
265  *      - no longer uses csp->init_proxy_args
266  *
267  *    - cgi_default:
268  *      - moved menu generation to make_menu()
269  *
270  *    - add_stats now writes single export map entries instead
271  *      of a fixed string
272  *
273  *    - Moved redirect_url() to filters.c
274  *
275  *    - Fixed mem leak in free_http_response(), map_block_killer(),
276  *
277  *    - Removed logentry from cancelled commit
278  *
279  *    Revision 1.7  2001/06/09 10:51:58  jongfoster
280  *    Changing "show URL info" handler to new style.
281  *    Changing BUFSIZ ==> BUFFER_SIZE
282  *
283  *    Revision 1.6  2001/06/07 23:05:19  jongfoster
284  *    Removing code related to old forward and ACL files.
285  *
286  *    Revision 1.5  2001/06/05 19:59:16  jongfoster
287  *    Fixing multiline character string (a GCC-only "feature"), and snprintf (it's _snprintf under VC++).
288  *
289  *    Revision 1.4  2001/06/04 10:41:52  swa
290  *    show version string of cgi.h and cgi.c
291  *
292  *    Revision 1.3  2001/06/03 19:12:16  oes
293  *    introduced new cgi handling
294  *
295  *    No revisions before 1.3
296  *
297  **********************************************************************/
298 \f
299
300 #include "config.h"
301
302 #include <stdio.h>
303 #include <sys/types.h>
304 #include <stdlib.h>
305 #include <ctype.h>
306 #include <string.h>
307 #include <assert.h>
308
309 #ifdef _WIN32
310 #define snprintf _snprintf
311 #endif /* def _WIN32 */
312
313 #include "project.h"
314 #include "cgi.h"
315 #include "list.h"
316 #include "encode.h"
317 #include "ssplit.h"
318 #include "errlog.h"
319 #include "miscutil.h"
320 #include "cgisimple.h"
321 #ifdef FEATURE_CGI_EDIT_ACTIONS
322 #include "cgiedit.h"
323 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
324 #include "loadcfg.h"
325 /* loadcfg.h is for g_bToggleIJB only */
326
327 const char cgi_h_rcs[] = CGI_H_VERSION;
328
329 static const struct cgi_dispatcher cgi_dispatchers[] = {
330    { "",
331          cgi_default,
332          "Junkbuster main page" },
333    { "show-status", 
334          cgi_show_status,  
335          "Show information about the current configuration" }, 
336    { "show-version", 
337          cgi_show_version,  
338          "Show the source code version numbers" }, 
339    { "show-request", 
340          cgi_show_request,  
341          "Show the client's request headers." }, 
342    { "show-url-info",
343          cgi_show_url_info, 
344          "Show which actions apply to a URL and why"  },
345    { "toggle",
346          cgi_toggle, 
347          "Toggle JunkBuster on or off" },
348 #ifdef FEATURE_CGI_EDIT_ACTIONS
349    { "edit-actions",
350          cgi_edit_actions, 
351          "Edit the actions list" },
352
353    
354    { "eaa", /* Shortcut for edit-actions-add-url-form */
355          cgi_edit_actions_add_url_form, 
356          NULL },
357    { "eau", /* Shortcut for edit-actions-url-form */
358          cgi_edit_actions_url_form, 
359          NULL },
360    { "ear", /* Shortcut for edit-actions-remove-url-form */
361          cgi_edit_actions_remove_url_form, 
362          NULL },
363    { "eas", /* Shortcut for edit-actions-for-url */
364          cgi_edit_actions_for_url, 
365          NULL },
366    { "easa", /* Shortcut for edit-actions-section-add */
367          cgi_edit_actions_section_add, 
368          NULL },
369    { "easr", /* Shortcut for edit-actions-section-remove */
370          cgi_edit_actions_section_remove, 
371          NULL },
372    { "eass", /* Shortcut for edit-actions-section-swap */
373          cgi_edit_actions_section_swap, 
374          NULL },
375    { "edit-actions-for-url",
376          cgi_edit_actions_for_url, 
377          NULL /* Edit the actions for (a) specified URL(s) */ },
378    { "edit-actions-list",
379          cgi_edit_actions_list, 
380          NULL /* Edit the actions list */ },
381    { "edit-actions-submit",
382          cgi_edit_actions_submit, 
383          NULL /* Change the actions for (a) specified URL(s) */ },
384    { "edit-actions-url",
385          cgi_edit_actions_url, 
386          NULL /* Change a URL pattern in the actionsfile */ },
387    { "edit-actions-url-form",
388          cgi_edit_actions_url_form, 
389          NULL /* Form to change a URL pattern in the actionsfile */ },
390    { "edit-actions-add-url",
391          cgi_edit_actions_add_url, 
392          NULL /* Add a URL pattern to the actionsfile */ },
393    { "edit-actions-add-url-form",
394          cgi_edit_actions_add_url_form, 
395          NULL /* Form to add a URL pattern to the actionsfile */ },
396    { "edit-actions-remove-url",
397          cgi_edit_actions_remove_url, 
398          NULL /* Remove a URL pattern from the actionsfile */ },
399    { "edit-actions-remove-url-form",
400          cgi_edit_actions_remove_url_form, 
401          NULL /* Form to remove a URL pattern from the actionsfile */ },
402    { "edit-actions-section-add",
403          cgi_edit_actions_section_add, 
404          NULL /* Remove a section from the actionsfile */ },
405    { "edit-actions-section-remove",
406          cgi_edit_actions_section_remove, 
407          NULL /* Remove a section from the actionsfile */ },
408    { "edit-actions-section-swap",
409          cgi_edit_actions_section_swap, 
410          NULL /* Swap two sections in the actionsfile */ },
411 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
412    { "robots.txt", 
413          cgi_robots_txt,  
414          NULL /* Sends a robots.txt file to tell robots to go away. */ }, 
415    { "send-banner",
416          cgi_send_banner, 
417          NULL /* Send a built-in image */ },
418    { "t",
419          cgi_transparent_image, 
420          NULL /* Send a transparent image (short name) */ },
421    { NULL, /* NULL Indicates end of list and default page */
422          cgi_error_404,
423          NULL /* Unknown CGI page */ }
424 };
425
426
427 /*
428  * Bulit-in images for ad replacement
429  *
430  * Hint: You can encode your own images like this:
431  * cat your-image | perl -e 'while (read STDIN, $c, 1) { printf("\\%.3o", unpack("C", $c)); }'
432  */
433
434 #ifdef FEATURE_NO_GIFS
435
436 /*
437  * "Junkbuster" logo, as a PNG.
438  */
439 const char image_logo_data[] =
440    "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104"
441    "\122\000\000\000\104\000\000\000\013\001\003\000\000\000\374"
442    "\323\271\110\000\000\000\004\147\101\115\101\000\000\261\217"
443    "\013\374\141\005\000\000\000\006\120\114\124\105\310\310\310"
444    "\000\000\000\062\100\100\345\000\000\000\001\164\122\116\123"
445    "\000\100\346\330\146\000\000\000\001\142\113\107\104\000\210"
446    "\005\035\110\000\000\000\126\111\104\101\124\170\332\143\140"
447    "\200\003\066\006\146\066\006\006\011\124\326\331\315\146\147"
448    "\316\333\174\116\000\262\156\247\347\334\234\071\373\001\220"
449    "\065\373\130\316\035\311\331\015\020\326\161\311\337\014\014"
450    "\151\147\147\247\345\034\236\331\014\144\335\235\155\236\173"
451    "\163\366\154\006\006\233\334\331\162\307\316\363\174\106\330"
452    "\300\000\000\002\127\040\324\250\204\044\102\000\000\000\000"
453    "\111\105\116\104\256\102\140\202";
454
455 /*
456  * Checkerboard pattern, as a PNG.
457  */
458 const char image_pattern_data[] =
459    "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104"
460    "\122\000\000\000\004\000\000\000\004\010\002\000\000\000\046"
461    "\223\011\051\000\000\000\006\142\113\107\104\000\310\000\310"
462    "\000\310\052\045\225\037\000\000\000\032\111\104\101\124\170"
463    "\332\143\070\161\342\304\377\377\377\041\044\003\234\165\342"
464    "\304\011\006\234\062\000\125\200\052\251\125\174\360\223\000"
465    "\000\000\000\111\105\116\104\256\102\140\202";
466
467 /*
468  * 1x1 transparant PNG.
469  */
470 const char image_blank_data[] =
471    "\211\120\116\107\015\012\032\012\000\000\000\015\111\110\104"
472    "\122\000\000\000\002\000\000\000\002\010\002\000\000\000\375"
473    "\324\232\163\000\000\000\006\142\113\107\104\000\310\000\310"
474    "\000\310\052\045\225\037\000\000\000\027\111\104\101\124\170"
475    "\332\143\374\377\377\377\311\223\047\031\116\234\070\361\377"
476    "\377\177\000\112\335\012\257\162\103\306\335\000\000\000\000"
477    "\111\105\116\104\256\102\140\202o";
478 #else
479
480 /*
481  * "Junkbuster" logo, as a GIF.
482  */
483 const char image_logo_data[] =
484    "GIF89aD\000\013\000\360\000\000\000\000\000\377\377\377!"
485    "\371\004\001\000\000\001\000,\000\000\000\000D\000\013\000"
486    "\000\002a\214\217\251\313\355\277\000\200G&K\025\316hC\037"
487    "\200\234\230Y\2309\235S\230\266\206\372J\253<\3131\253\271"
488    "\270\215\342\254\013\203\371\202\264\334P\207\332\020o\266"
489    "N\215I\332=\211\312\3513\266:\026AK)\364\370\365aobr\305"
490    "\372\003S\275\274k2\354\254z\347?\335\274x\306^9\374\276"
491    "\037Q\000\000;";
492
493 /*
494  * Checkerboard pattern, as a GIF.
495  */
496 const char image_pattern_data[] =
497    "\107\111\106\070\071\141\004\000\004\000\200\000\000\310\310"
498    "\310\377\377\377\041\376\025\111\040\165\163\145\144\040\164"
499    "\157\040\142\145\040\141\040\142\141\156\156\145\162\000\054"
500    "\000\000\000\000\004\000\004\000\000\002\005\104\174\147\270"
501    "\005\000\073";
502
503 /*
504  * 1x1 transparant GIF.
505  */
506 const char image_blank_data[] =
507    "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000"
508    "\000!\371\004\001\000\000\000\000,\000\000\000\000\001"
509    "\000\001\000\000\002\002D\001\000;";
510 #endif
511
512 const size_t image_logo_length    = sizeof(image_logo_data) - 1;
513 const size_t image_pattern_length = sizeof(image_pattern_data) - 1;
514 const size_t image_blank_length   = sizeof(image_blank_data) - 1;
515
516
517 static struct http_response cgi_error_memory_response[1];
518
519 static struct http_response *dispatch_known_cgi(struct client_state * csp,
520                                                 const char * path);
521 static struct map *parse_cgi_parameters(char *argstring);
522
523
524 /*********************************************************************
525  * 
526  * Function    :  dispatch_cgi
527  *
528  * Description :  Checks if a request URL has either the magical
529  *                hostname CGI_SITE_1_HOST (usully http://i.j.b/) or
530  *                matches CGI_SITE_2_HOST CGI_SITE_2_PATH (usually
531  *                http://ijbswa.sourceforge.net/config). If so, it passes
532  *                the (rest of the) path onto dispatch_known_cgi, which
533  *                calls the relevant CGI handler function.
534  *
535  * Parameters  :
536  *          1  :  csp = Current client state (buffers, headers, etc...)
537  *
538  * Returns     :  http_response if match, NULL if nonmatch or handler fail
539  *
540  *********************************************************************/
541 struct http_response *dispatch_cgi(struct client_state *csp)
542 {
543    const char *host = csp->http->host;
544    const char *path = csp->http->path;
545
546    /*
547     * Should we intercept ?
548     */
549
550    /* Note: "example.com" and "example.com." are equivalent hostnames. */
551
552    /* Either the host matches CGI_SITE_1_HOST ..*/
553    if (   ( (0 == strcmpic(host, CGI_SITE_1_HOST))
554          || (0 == strcmpic(host, CGI_SITE_1_HOST ".")))
555        && (path[0] == '/') )
556    {
557       /* ..then the path will all be for us.  Remove leading '/' */
558       path++;
559    }
560    /* Or it's the host part CGI_SITE_2_HOST, and the path CGI_SITE_2_PATH */
561    else if ( ( (0 == strcmpic(host, CGI_SITE_2_HOST ))
562             || (0 == strcmpic(host, CGI_SITE_2_HOST ".")) )
563           && (0 == strncmpic(path, CGI_SITE_2_PATH, strlen(CGI_SITE_2_PATH))) )
564    {
565       /* take everything following CGI_SITE_2_PATH */
566       path += strlen(CGI_SITE_2_PATH);
567       if (*path == '/')
568       {
569          /* skip the forward slash after CGI_SITE_2_PATH */
570          path++;
571       }
572       else if (*path != '\0')
573       {
574          /*
575           * wierdness: URL is /configXXX, where XXX is some string
576           * Do *NOT* intercept.
577           */
578          return NULL;
579       }
580    }
581    else
582    {
583       /* Not a CGI */
584       return NULL;
585    }
586
587    /* 
588     * This is a CGI call.
589     */
590
591    return dispatch_known_cgi(csp, path);
592 }
593
594
595 /*********************************************************************
596  * 
597  * Function    :  dispatch_known_cgi
598  *
599  * Description :  Processes a CGI once dispatch_cgi has determined that
600  *                it matches one of the magic prefixes. Parses the path
601  *                as a cgi name plus query string, prepares a map that
602  *                maps CGI parameter names to their values, initializes
603  *                the http_response struct, and calls the relevant CGI
604  *                handler function.
605  *
606  * Parameters  :
607  *          1  :  csp = Current client state (buffers, headers, etc...)
608  *          2  :  path = Path of CGI, with the CGI prefix removed.
609  *                       Should not have a leading "/".
610  *
611  * Returns     :  http_response, or NULL on handler failure or out of
612  *                memory.
613  *
614  *********************************************************************/
615 static struct http_response *dispatch_known_cgi(struct client_state * csp,
616                                                 const char * path)
617 {
618    const struct cgi_dispatcher *d;
619    struct map *param_list;
620    struct http_response *rsp;
621    char *query_args_start;
622    char *path_copy;
623    jb_err err;
624
625    if (NULL == (path_copy = strdup(path)))
626    {
627       return cgi_error_memory();
628    }
629
630    query_args_start = path_copy;
631    while (*query_args_start && *query_args_start != '?')
632    {
633       query_args_start++;
634    }
635    if (*query_args_start == '?')
636    {
637       *query_args_start++ = '\0';
638    }
639
640    if (NULL == (param_list = parse_cgi_parameters(query_args_start)))
641    {
642       free(path_copy);
643       return cgi_error_memory();
644    }
645
646
647    /*
648     * At this point:
649     * path_copy        = CGI call name
650     * param_list       = CGI params, as map
651     */
652
653    /* Get mem for response or fail*/
654    if (NULL == (rsp = alloc_http_response()))
655    {
656       free(path_copy);
657       free_map(param_list);
658       return cgi_error_memory();
659    }
660
661    log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
662    log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3", 
663                             csp->ip_addr_str, csp->http->cmd); 
664
665    /* Find and start the right CGI function*/
666    d = cgi_dispatchers;
667    for (;;)
668    {
669       if ((d->name == NULL) || (strcmp(path_copy, d->name) == 0))
670       {
671          err = (d->handler)(csp, rsp, param_list);
672          free(path_copy);
673          free_map(param_list);
674          if (err == JB_ERR_CGI_PARAMS)
675          {
676             err = cgi_error_bad_param(csp, rsp);
677          }
678          if (!err)
679          {
680             /* It worked */
681             return finish_http_response(rsp);
682          }
683          else
684          {
685             /* Error in handler, probably out-of-memory */
686             free_http_response(rsp);
687             return cgi_error_memory();
688          }
689       }
690       d++;
691    }
692 }
693
694
695 /*********************************************************************
696  *
697  * Function    :  parse_cgi_parameters
698  *
699  * Description :  Parse a URL-encoded argument string into name/value
700  *                pairs and store them in a struct map list.
701  *
702  * Parameters  :
703  *          1  :  string = string to be parsed.  Will be trashed.
704  *
705  * Returns     :  pointer to param list, or NULL if out of memory.
706  *
707  *********************************************************************/
708 static struct map *parse_cgi_parameters(char *argstring)
709 {
710    char *p;
711    char *vector[BUFFER_SIZE];
712    int pairs, i;
713    struct map *cgi_params;
714
715    if (NULL == (cgi_params = new_map()))
716    {
717       return NULL;
718    }
719
720    pairs = ssplit(argstring, "&", vector, SZ(vector), 1, 1);
721
722    for (i = 0; i < pairs; i++)
723    {
724       if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
725       {
726          *p = '\0';
727          if (map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0))
728          {
729             free_map(cgi_params);
730             return NULL;
731          }
732       }
733    }
734
735    return cgi_params;
736
737 }
738
739
740 /*********************************************************************
741  *
742  * Function    :  error_response
743  *
744  * Description :  returns an http_response that explains the reason
745  *                why a request failed.
746  *
747  * Parameters  :
748  *          1  :  csp = Current client state (buffers, headers, etc...)
749  *          2  :  templatename = Which template should be used for the answer
750  *          3  :  sys_err = system error number
751  *
752  * Returns     :  A http_response.  If we run out of memory, this
753  *                will be cgi_error_memory().
754  *
755  *********************************************************************/
756 struct http_response *error_response(struct client_state *csp,
757                                      const char *templatename,
758                                      int sys_err)
759 {
760    jb_err err;
761    struct http_response *rsp;
762    struct map * exports = default_exports(csp, NULL);
763    if (exports == NULL)
764    {
765       return cgi_error_memory();
766    }
767
768    if (NULL == (rsp = alloc_http_response()))
769    {
770       free_map(exports);
771       return cgi_error_memory();
772    }
773
774    err = map(exports, "host", 1, html_encode(csp->http->host), 0);
775    if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
776    if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
777    if (!err) err = map(exports, "error", 1, html_encode_and_free_original(safe_strerror(sys_err)), 0);
778    if (!err)
779    {
780      err = map(exports, "host-ip", 1, html_encode(csp->http->host_ip_addr_str), 0);
781      if (err)
782      {
783        /* Some failures, like "404 no such domain", don't have an IP address. */
784        err = map(exports, "host-ip", 1, html_encode(csp->http->host), 0);
785      }
786    }
787
788
789    if (err)
790    {
791       free_map(exports);
792       free_http_response(rsp);
793       return cgi_error_memory();
794    }
795
796    if (!strcmp(templatename, "no-such-domain"))
797    {
798       rsp->status = strdup("404 No such domain");
799       if (rsp->status == NULL)
800       {
801          free_map(exports);
802          free_http_response(rsp);
803          return cgi_error_memory();
804       }
805    }
806    else if (!strcmp(templatename, "connect-failed"))
807    {
808       rsp->status = strdup("503 Connect failed");
809       if (rsp->status == NULL)
810       {
811          free_map(exports);
812          free_http_response(rsp);
813          return cgi_error_memory();
814       }
815    }
816
817    err = template_fill_for_cgi(csp, templatename, exports, rsp);
818    if (err)
819    {
820       free_http_response(rsp);
821       return cgi_error_memory();
822    }
823
824    return finish_http_response(rsp);
825 }
826
827
828 /*********************************************************************
829  *
830  * Function    :  cgi_init_error_messages
831  *
832  * Description :  Call at the start of the program to initialize
833  *                the error message used by cgi_error_memory().
834  *
835  * Parameters  :  N/A
836  *
837  * Returns     :  N/A
838  *
839  *********************************************************************/
840 void cgi_init_error_messages(void)
841 {
842    memset(cgi_error_memory_response, '\0', sizeof(*cgi_error_memory_response));
843    cgi_error_memory_response->head =
844       "HTTP/1.0 500 Internal JunkBuster Proxy Error\r\n"
845       "Content-Type: text/html\r\n"
846       "\r\n";
847    cgi_error_memory_response->body =
848       "<html>\r\n"
849       "<head><title>500 Internal JunkBuster Proxy Error</title></head>\r\n"
850       "<body>\r\n"
851       "<h1>500 Internal JunkBuster Proxy Error</h1>\r\n"
852       "<p>JunkBuster <b>ran out of memory</b> while processing your request.</p>\r\n"
853       "<p>Please contact your proxy administrator, or try again later</p>\r\n"
854       "</body>\r\n"
855       "</html>\r\n";
856
857    cgi_error_memory_response->head_length =
858       strlen(cgi_error_memory_response->head);
859    cgi_error_memory_response->content_length =
860       strlen(cgi_error_memory_response->body);
861 }
862
863
864 /*********************************************************************
865  *
866  * Function    :  cgi_error_memory
867  *
868  * Description :  Called if a CGI function runs out of memory.
869  *                Returns a statically-allocated error response.
870  *
871  * Parameters  :
872  *          1  :  csp = Current client state (buffers, headers, etc...)
873  *          2  :  rsp = http_response data structure for output
874  *          3  :  template_name = Name of template that could not
875  *                                be loaded.
876  *
877  * Returns     :  JB_ERR_OK on success
878  *                JB_ERR_MEMORY on out-of-memory error.  
879  *
880  *********************************************************************/
881 struct http_response *cgi_error_memory(void)
882 {
883    /* assert that it's been initialized. */
884    assert(cgi_error_memory_response->head);
885
886    return cgi_error_memory_response;
887 }
888
889
890 /*********************************************************************
891  *
892  * Function    :  cgi_error_no_template
893  *
894  * Description :  Almost-CGI function that is called if a templae
895  *                cannot be loaded.  Note this is not a true CGI,
896  *                it takes a template name rather than a map of 
897  *                parameters.
898  *
899  * Parameters  :
900  *          1  :  csp = Current client state (buffers, headers, etc...)
901  *          2  :  rsp = http_response data structure for output
902  *          3  :  template_name = Name of template that could not
903  *                                be loaded.
904  *
905  * Returns     :  JB_ERR_OK on success
906  *                JB_ERR_MEMORY on out-of-memory error.  
907  *
908  *********************************************************************/
909 jb_err cgi_error_no_template(struct client_state *csp,
910                              struct http_response *rsp,
911                              const char *template_name)
912 {
913    static const char status[] =
914       "500 Internal JunkBuster Proxy Error";
915    static const char body_prefix[] =
916       "<html>\r\n"
917       "<head><title>500 Internal JunkBuster Proxy Error</title></head>\r\n"
918       "<body>\r\n"
919       "<h1>500 Internal JunkBuster Proxy Error</h1>\r\n"
920       "<p>JunkBuster encountered an error while processing your request:</p>\r\n"
921       "<p><b>Could not load template file <code>";
922    static const char body_suffix[] =
923       "</code></b></p>\r\n"
924       "<p>Please contact your proxy administrator.</p>\r\n"
925       "<p>If you are the proxy administrator, please put the required file "
926       "in the <code><i>(confdir)</i>/templates</code> directory.  The "
927       "location of the <code><i>(confdir)</i></code> directory "
928       "is specified in the main JunkBuster <code>config</code> "
929       "file.  (It's typically the JunkBuster install directory"
930 #ifndef _WIN32
931       ", or <code>/etc/junkbuster/</code>"
932 #endif /* ndef _WIN32 */
933       ").</p>\r\n"
934       "</body>\r\n"
935       "</html>\r\n";
936
937    assert(csp);
938    assert(rsp);
939    assert(template_name);
940
941    /* Reset rsp, if needed */
942    freez(rsp->status);
943    freez(rsp->head);
944    freez(rsp->body);
945    rsp->content_length = 0;
946    rsp->head_length = 0;
947    rsp->is_static = 0;
948
949    rsp->body = malloc(strlen(body_prefix) + strlen(template_name) + strlen(body_suffix) + 1);
950    if (rsp->body == NULL)
951    {
952       return JB_ERR_MEMORY;
953    }
954    strcpy(rsp->body, body_prefix);
955    strcat(rsp->body, template_name);
956    strcat(rsp->body, body_suffix);
957
958    rsp->status = strdup(status);
959    if (rsp->body == NULL)
960    {
961       return JB_ERR_MEMORY;
962    }
963
964    return JB_ERR_OK;
965 }
966
967
968 /*********************************************************************
969  *
970  * Function    :  cgi_error_bad_param
971  *
972  * Description :  CGI function that is called if the parameters
973  *                (query string) for a CGI were wrong.
974  *               
975  * Parameters  :
976  *          1  :  csp = Current client state (buffers, headers, etc...)
977  *          2  :  rsp = http_response data structure for output
978  *
979  * CGI Parameters : none
980  *
981  * Returns     :  JB_ERR_OK on success
982  *                JB_ERR_MEMORY on out-of-memory error.  
983  *
984  *********************************************************************/
985 jb_err cgi_error_bad_param(struct client_state *csp,
986                            struct http_response *rsp)
987 {
988    struct map *exports;
989
990    assert(csp);
991    assert(rsp);
992
993    if (NULL == (exports = default_exports(csp, NULL)))
994    {
995       return JB_ERR_MEMORY;
996    }
997
998    return template_fill_for_cgi(csp, "cgi-error-bad-param", exports, rsp);
999 }
1000
1001
1002 /*********************************************************************
1003  *
1004  * Function    :  get_http_time
1005  *
1006  * Description :  Get the time in a format suitable for use in a
1007  *                HTTP header - e.g.:
1008  *                "Sun, 06 Nov 1994 08:49:37 GMT"
1009  *
1010  * Parameters  :  
1011  *          1  :  time_offset = Time returned will be current time
1012  *                              plus this number of seconds.
1013  *          2  :  buf = Destination for result.  Must be long enough
1014  *                      to hold 29 characters plus a trailing zero.
1015  *
1016  * Returns     :  N/A
1017  *
1018  *********************************************************************/
1019 void get_http_time(int time_offset, char *buf)
1020 {
1021    static const char day_names[7][4] =
1022       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
1023    static const char month_names[12][4] =
1024       { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1025         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1026
1027    struct tm *t;
1028    time_t current_time;
1029
1030    assert(buf);
1031
1032    time(&current_time); /* get current time */
1033
1034    current_time += time_offset;
1035
1036    /* get and save the gmt */
1037    {
1038 #ifdef HAVE_GMTIME_R
1039       struct tm dummy;
1040       t = gmtime_r(&current_time, &dummy);
1041 #else
1042       t = gmtime(&current_time);
1043 #endif
1044    }
1045
1046    /* Format: "Sun, 06 Nov 1994 08:49:37 GMT" */
1047    snprintf(buf, 30,
1048       "%s, %02d %s %4d %02d:%02d:%02d GMT",
1049       day_names[t->tm_wday],
1050       t->tm_mday,
1051       month_names[t->tm_mon],
1052       t->tm_year + 1900,
1053       t->tm_hour,
1054       t->tm_min,
1055       t->tm_sec
1056       );
1057
1058 }
1059
1060
1061 /*********************************************************************
1062  *
1063  * Function    :  finish_http_response
1064  *
1065  * Description :  Fill in the missing headers in an http response,
1066  *                and flatten the headers to an http head.
1067  *
1068  * Parameters  :
1069  *          1  :  rsp = pointer to http_response to be processed
1070  *
1071  * Returns     :  A http_response, usually the rsp parameter.
1072  *                On error, free()s rsp and returns cgi_error_memory()
1073  *
1074  *********************************************************************/
1075 struct http_response *finish_http_response(struct http_response *rsp)
1076 {
1077    char buf[BUFFER_SIZE];
1078    jb_err err;
1079
1080    /* Special case - do NOT change this statically allocated response,
1081     * which is ready for output anyway.
1082     */
1083    if (rsp == cgi_error_memory_response)
1084    {
1085       return rsp;
1086    }
1087
1088    /* 
1089     * Fill in the HTTP Status
1090     */
1091    sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK");
1092    err = enlist_first(rsp->headers, buf);
1093
1094    /* 
1095     * Set the Content-Length
1096     */
1097    if (rsp->content_length == 0)
1098    {
1099       rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
1100    }
1101    if (!err)
1102    {
1103       sprintf(buf, "Content-Length: %d", rsp->content_length);
1104       err = enlist(rsp->headers, buf);
1105    }
1106
1107    /* 
1108     * Fill in the default headers:
1109     *
1110     * Content-Type: default to text/html if not already specified.
1111     * Date: set to current date/time.
1112     * Last-Modified: set to date/time the page was last changed.
1113     * Expires: set to date/time page next needs reloading.
1114     * Cache-Control: set to "no-cache" if applicable.
1115     * 
1116     * See http://www.w3.org/Protocols/rfc2068/rfc2068
1117     */
1118    if (!err) err = enlist_unique(rsp->headers, "Content-Type: text/html", 13);
1119
1120    if (rsp->is_static)
1121    {
1122       /*
1123        * Set Expires to about 10 min into the future so it'll get reloaded
1124        * occasionally, e.g. if IJB gets upgraded.
1125        */
1126
1127       if (!err)
1128       {
1129          get_http_time(0, buf);
1130          err = enlist_unique_header(rsp->headers, "Date", buf);
1131       }
1132
1133       /* Some date in the past. */
1134       if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", "Sat, 17 Jun 2000 12:00:00 GMT");
1135
1136       if (!err)
1137       {
1138          get_http_time(10 * 60, buf); /* 10 * 60sec = 10 minutes */
1139          err = enlist_unique_header(rsp->headers, "Expires", buf);
1140       }
1141    }
1142    else
1143    {
1144       /*
1145        * Compliant browsers should not cache this due to the "Cache-Control"
1146        * setting.  However, to be certain, we also set both "Last-Modified"
1147        * and "Expires" to the current time.
1148        */
1149       if (!err) err = enlist_unique_header(rsp->headers, "Cache-Control", "no-cache");
1150
1151       get_http_time(0, buf);
1152       if (!err) err = enlist_unique_header(rsp->headers, "Date", buf);
1153       if (!err) err = enlist_unique_header(rsp->headers, "Last-Modified", buf);
1154       if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
1155    }
1156
1157
1158    /* 
1159     * Write the head
1160     */
1161    if (err || (NULL == (rsp->head = list_to_text(rsp->headers))))
1162    {
1163       free_http_response(rsp);
1164       return cgi_error_memory();
1165    }
1166    rsp->head_length = strlen(rsp->head);
1167
1168    return rsp;
1169
1170 }
1171
1172
1173 /*********************************************************************
1174  *
1175  * Function    :  alloc_http_response
1176  *
1177  * Description :  Allocates a new http_response structure.
1178  *
1179  * Parameters  :  N/A
1180  *
1181  * Returns     :  pointer to a new http_response, or NULL.
1182  *
1183  *********************************************************************/
1184 struct http_response *alloc_http_response(void)
1185 {
1186    return (struct http_response *) zalloc(sizeof(struct http_response));
1187
1188 }
1189
1190
1191 /*********************************************************************
1192  *
1193  * Function    :  free_http_response
1194  *
1195  * Description :  Free the memory occupied by an http_response
1196  *                and its depandant structures.
1197  *
1198  * Parameters  :
1199  *          1  :  rsp = pointer to http_response to be freed
1200  *
1201  * Returns     :  N/A
1202  *
1203  *********************************************************************/
1204 void free_http_response(struct http_response *rsp)
1205 {
1206    /*
1207     * Must special case cgi_error_memory_response, which is never freed.
1208     */
1209    if (rsp && (rsp != cgi_error_memory_response))
1210    {
1211       freez(rsp->status);
1212       freez(rsp->head);
1213       freez(rsp->body);
1214       destroy_list(rsp->headers);
1215       free(rsp);
1216    }
1217
1218 }
1219
1220
1221 /*********************************************************************
1222  *
1223  * Function    :  template_load
1224  *
1225  * Description :  CGI support function that loads a given HTML
1226  *                template from the confdir, ignoring comment
1227  *                lines. 
1228  *
1229  * Parameters  :
1230  *          1  :  csp = Current client state (buffers, headers, etc...)
1231  *          2  :  template_ptr = Destination for pointer to loaded
1232  *                               template text.
1233  *          3  :  template = name of the HTML template to be used
1234  *
1235  * Returns     :  JB_ERR_OK on success
1236  *                JB_ERR_MEMORY on out-of-memory error.  
1237  *                JB_ERR_FILE if the template file cannot be read
1238  *
1239  *********************************************************************/
1240 jb_err template_load(struct client_state *csp, char ** template_ptr, 
1241                      const char *templatename)
1242 {
1243    char *templates_dir_path;
1244    char *full_path;
1245    char *file_buffer;
1246    FILE *fp;
1247    char buf[BUFFER_SIZE];
1248
1249    assert(csp);
1250    assert(template_ptr);
1251    assert(templatename);
1252
1253    *template_ptr = NULL;
1254
1255    /*
1256     * Open template file or fail
1257     */
1258
1259    templates_dir_path = make_path(csp->config->confdir, "templates");
1260    if (templates_dir_path == NULL)
1261    {
1262       return JB_ERR_MEMORY;
1263    }
1264
1265    full_path = make_path(templates_dir_path, templatename);
1266    free(templates_dir_path);
1267    if (full_path == NULL)
1268    {
1269       return JB_ERR_MEMORY;
1270    }
1271
1272    file_buffer = strdup("");
1273    if (file_buffer == NULL)
1274    {
1275       free(full_path);
1276       return JB_ERR_MEMORY;
1277    }
1278
1279    if (NULL == (fp = fopen(full_path, "r")))
1280    {
1281       log_error(LOG_LEVEL_ERROR, "Cannot open template file %s: %E", full_path);
1282       free(full_path);
1283       free(file_buffer);
1284       return JB_ERR_FILE;
1285    }
1286    free(full_path);
1287
1288    /* 
1289     * Read the file, ignoring comments.
1290     *
1291     * FIXME: The comment handling could break with lines >BUFFER_SIZE long.
1292     *        This is unlikely in practise.
1293     */
1294    while (fgets(buf, BUFFER_SIZE, fp))
1295    {
1296       /* skip lines starting with '#' */
1297       if(*buf == '#')
1298       {
1299          continue;
1300       }
1301
1302       if (string_append(&file_buffer, buf))
1303       {
1304          fclose(fp);
1305          return JB_ERR_MEMORY;
1306       }
1307    }
1308    fclose(fp);
1309
1310    *template_ptr = file_buffer;
1311
1312    return JB_ERR_OK;
1313 }
1314
1315
1316 /*********************************************************************
1317  *
1318  * Function    :  template_fill
1319  *
1320  * Description :  CGI support function that fills in a pre-loaded
1321  *                HTML template by replacing @name@ with value using
1322  *                pcrs, for each item in the output map.
1323  *
1324  *                Note that a leading '$' charachter in the export map's
1325  *                values will be stripped and toggle on backreference
1326  *                interpretation.
1327  *
1328  * Parameters  :
1329  *          1  :  template_ptr = IN: Template to be filled out.
1330  *                                   Will be free()d.
1331  *                               OUT: Filled out template.
1332  *                                    Caller must free().
1333  *          2  :  exports = map with fill in symbol -> name pairs
1334  *
1335  * Returns     :  JB_ERR_OK on success
1336  *                JB_ERR_MEMORY on out-of-memory error
1337  *
1338  *********************************************************************/
1339 jb_err template_fill(char **template_ptr, const struct map *exports)
1340 {
1341    struct map_entry *m;
1342    pcrs_job *job;
1343    char buf[BUFFER_SIZE];
1344    char *tmp_out_buffer;
1345    char *file_buffer;
1346    size_t  size;
1347    int error;
1348    const char *flags;
1349
1350    assert(template_ptr);
1351    assert(*template_ptr);
1352    assert(exports);
1353
1354    file_buffer = *template_ptr;
1355    size = strlen(file_buffer) + 1;
1356
1357    /* 
1358     * Assemble pcrs joblist from exports map
1359     */
1360    for (m = exports->first; m != NULL; m = m->next)
1361    {
1362       if (*m->name == '$')
1363       {
1364          /*
1365           * First character of name is '$', so remove this flag
1366           * character and allow backreferences ($1 etc) in the
1367           * "replace with" text.
1368           */
1369          snprintf(buf, BUFFER_SIZE, "%s", m->name + 1);
1370          flags = "sigU";
1371       }
1372       else
1373       {
1374          /*
1375           * Treat the "replace with" text as a literal string - 
1376           * no quoting needed, no backreferences allowed.
1377           * ("Trivial" ['T'] flag).
1378           */
1379          flags = "sigTU";
1380
1381          /* Enclose name in @@ */
1382          snprintf(buf, BUFFER_SIZE, "@%s@", m->name);
1383       }
1384
1385
1386       log_error(LOG_LEVEL_CGI, "Substituting: s/%s/%s/%s", buf, m->value, flags);
1387
1388       /* Make and run job. */
1389       job = pcrs_compile(buf, m->value, flags,  &error);
1390       if (job == NULL) 
1391       {
1392          if (error == PCRS_ERR_NOMEM)
1393          {
1394             free(file_buffer);
1395             *template_ptr = NULL;
1396             return JB_ERR_MEMORY;
1397          }
1398          else
1399          {
1400             log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error);
1401             /* Hope it wasn't important and silently ignore the invalid job */
1402          }
1403       }
1404       else
1405       {
1406          pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size);
1407          free(file_buffer);
1408          pcrs_free_job(job);
1409          if (NULL == tmp_out_buffer)
1410          {
1411             *template_ptr = NULL;
1412             return JB_ERR_MEMORY;
1413          }
1414          file_buffer = tmp_out_buffer;
1415       }
1416    }
1417
1418    /*
1419     * Return
1420     */
1421    *template_ptr = file_buffer;
1422    return JB_ERR_OK;
1423 }
1424
1425
1426 /*********************************************************************
1427  *
1428  * Function    :  template_fill_for_cgi
1429  *
1430  * Description :  CGI support function that loads a HTML template
1431  *                and fills it in.  Handles file-not-found errors
1432  *                by sending a HTML error message.  For convenience,
1433  *                this function also frees the passed "exports" map.
1434  *
1435  * Parameters  :
1436  *          1  :  csp = Client state
1437  *          2  :  templatename = name of the HTML template to be used
1438  *          3  :  exports = map with fill in symbol -> name pairs.
1439  *                          Will be freed by this function.
1440  *
1441  * Returns     :  JB_ERR_OK on success
1442  *                JB_ERR_MEMORY on out-of-memory error
1443  *
1444  *********************************************************************/
1445 jb_err template_fill_for_cgi(struct client_state *csp,
1446                              const char *templatename,
1447                              struct map *exports,
1448                              struct http_response *rsp)
1449 {
1450    jb_err err;
1451    
1452    assert(csp);
1453    assert(templatename);
1454    assert(exports);
1455    assert(rsp);
1456
1457    err = template_load(csp, &rsp->body, templatename);
1458    if (err == JB_ERR_FILE)
1459    {
1460       free_map(exports);
1461       return cgi_error_no_template(csp, rsp, templatename);
1462    }
1463    else if (err)
1464    {
1465       free_map(exports);
1466       return err; /* JB_ERR_MEMORY */
1467    }
1468    err = template_fill(&rsp->body, exports);
1469    free_map(exports);
1470    return err;
1471 }
1472
1473
1474 /*********************************************************************
1475  *
1476  * Function    :  default_exports
1477  *
1478  * Description :  returns a struct map list that contains exports
1479  *                which are common to all CGI functions.
1480  *
1481  * Parameters  :
1482  *          1  :  csp = Current client state (buffers, headers, etc...)
1483  *          2  :  caller = name of CGI who calls us and which should
1484  *                         be excluded from the generated menu. May be
1485  *                         NULL.
1486  * Returns     :  NULL if no memory, else a new map.  Caller frees.
1487  *
1488  *********************************************************************/
1489 struct map *default_exports(const struct client_state *csp, const char *caller)
1490 {
1491    char buf[20];
1492    jb_err err;
1493    struct map * exports;
1494    int local_help_exists = 0;
1495
1496    assert(csp);
1497
1498    exports = new_map();
1499    if (exports == NULL)
1500    {
1501       return NULL;
1502    }
1503
1504    err = map(exports, "version", 1, html_encode(VERSION), 0);
1505    if (!err) err = map(exports, "my-ip-address", 1, html_encode(csp->my_ip_addr_str ? csp->my_ip_addr_str : "unknown"), 0);
1506    if (!err) err = map(exports, "my-hostname",   1, html_encode(csp->my_hostname ? csp->my_hostname : "unknown"), 0);
1507    if (!err) err = map(exports, "homepage",      1, html_encode(HOME_PAGE_URL), 0);
1508    if (!err) err = map(exports, "default-cgi",   1, html_encode(CGI_PREFIX), 0);
1509    if (!err) err = map(exports, "menu",          1, make_menu(caller), 0);
1510    if (!err) err = map(exports, "code-status",   1, CODE_STATUS, 1);
1511    if (!err) err = map_conditional(exports, "enabled-display", g_bToggleIJB);
1512
1513    snprintf(buf, 20, "%d", csp->config->hport);
1514    if (!err) err = map(exports, "my-port", 1, buf, 1);
1515
1516    if(!strcmp(CODE_STATUS, "stable"))
1517    {
1518       if (!err) err = map_block_killer(exports, "unstable");
1519    }
1520
1521    if (csp->config->admin_address != NULL)
1522    {
1523       if (!err) err = map(exports, "admin-address", 1, html_encode(csp->config->admin_address), 0);
1524       local_help_exists = 1;
1525    }
1526    else
1527    {
1528       if (!err) err = map_block_killer(exports, "have-adminaddr-info");
1529    }
1530
1531    if (csp->config->proxy_info_url != NULL)
1532    {
1533       if (!err) err = map(exports, "proxy-info-url", 1, html_encode(csp->config->proxy_info_url), 0);
1534       local_help_exists = 1;
1535    }
1536    else
1537    {
1538       if (!err) err = map_block_killer(exports, "have-proxy-info");
1539    }
1540
1541    if (local_help_exists == 0)
1542    {
1543       if (!err) err = map_block_killer(exports, "have-help-info");
1544    }
1545
1546    if (err)
1547    {
1548       free_map(exports);
1549       return NULL;
1550    }
1551
1552    return exports;
1553 }
1554
1555
1556 /*********************************************************************
1557  *
1558  * Function    :  map_block_killer
1559  *
1560  * Description :  Convenience function.
1561  *                Adds a "killer" for the conditional HTML-template
1562  *                block <name>, i.e. a substitution of the regex
1563  *                "if-<name>-start.*if-<name>-end" to the given
1564  *                export list.
1565  *
1566  * Parameters  :  
1567  *          1  :  exports = map to extend
1568  *          2  :  name = name of conditional block
1569  *
1570  * Returns     :  JB_ERR_OK on success
1571  *                JB_ERR_MEMORY on out-of-memory error.  
1572  *
1573  *********************************************************************/
1574 jb_err map_block_killer(struct map *exports, const char *name)
1575 {
1576    char buf[1000]; /* Will do, since the names are hardwired */
1577
1578    assert(exports);
1579    assert(name);
1580    assert(strlen(name) < 490);
1581
1582    snprintf(buf, 1000, "if-%s-start.*if-%s-end", name, name);
1583    return map(exports, buf, 1, "", 1);
1584 }
1585
1586
1587 /*********************************************************************
1588  *
1589  * Function    :  map_block_keep
1590  *
1591  * Description :  Convenience function.  Removes the markers used
1592  *                by map-block-killer, to save a few bytes.
1593  *                i.e. removes "@if-<name>-start@" and "@if-<name>-end@"
1594  *
1595  * Parameters  :  
1596  *          1  :  exports = map to extend
1597  *          2  :  name = name of conditional block
1598  *
1599  * Returns     :  JB_ERR_OK on success
1600  *                JB_ERR_MEMORY on out-of-memory error.  
1601  *
1602  *********************************************************************/
1603 jb_err map_block_keep(struct map *exports, const char *name)
1604 {
1605    jb_err err;
1606    char buf[500]; /* Will do, since the names are hardwired */
1607
1608    assert(exports);
1609    assert(name);
1610    assert(strlen(name) < 490);
1611
1612    snprintf(buf, 500, "if-%s-start", name);
1613    err = map(exports, buf, 1, "", 1);
1614
1615    if (err)
1616    {
1617       return err;
1618    }
1619
1620    snprintf(buf, 500, "if-%s-end", name);
1621    return map(exports, buf, 1, "", 1);
1622 }
1623
1624
1625 /*********************************************************************
1626  *
1627  * Function    :  map_conditional
1628  *
1629  * Description :  Convenience function.
1630  *                Adds an "if-then-else" for the conditional HTML-template
1631  *                block <name>, i.e. a substitution of the form:
1632  *                @if-<name>-then@
1633  *                   True text
1634  *                @else-not-<name>@
1635  *                   False text
1636  *                @endif-<name>@
1637  *
1638  *                The control structure and one of the alternatives
1639  *                will be hidden.
1640  *
1641  * Parameters  :  
1642  *          1  :  exports = map to extend
1643  *          2  :  name = name of conditional block
1644  *          3  :  choose_first = nonzero for first, zero for second.
1645  *
1646  * Returns     :  JB_ERR_OK on success
1647  *                JB_ERR_MEMORY on out-of-memory error.  
1648  *
1649  *********************************************************************/
1650 jb_err map_conditional(struct map *exports, const char *name, int choose_first)
1651 {
1652    char buf[1000]; /* Will do, since the names are hardwired */
1653    jb_err err;
1654
1655    assert(exports);
1656    assert(name);
1657    assert(strlen(name) < 480);
1658
1659    snprintf(buf, 1000, (choose_first
1660       ? "else-not-%s@.*@endif-%s"
1661       : "if-%s-then@.*@else-not-%s"),
1662       name, name);
1663
1664    err = map(exports, buf, 1, "", 1);
1665    if (err)
1666    {
1667       return err;
1668    }
1669
1670    snprintf(buf, 1000, (choose_first ? "if-%s-then" : "endif-%s"), name);
1671    return map(exports, buf, 1, "", 1);
1672 }
1673
1674
1675 /*********************************************************************
1676  *
1677  * Function    :  make_menu
1678  *
1679  * Description :  Returns an HTML-formatted menu of the available 
1680  *                unhidden CGIs, excluding the one given in <self>.
1681  *
1682  * Parameters  :  self = name of CGI to leave out, can be NULL
1683  *
1684  * Returns     :  menu string, or NULL on out-of-memory error.
1685  *
1686  *********************************************************************/
1687 char *make_menu(const char *self)
1688 {
1689    const struct cgi_dispatcher *d;
1690    char *result = strdup("");
1691
1692    if (self == NULL)
1693    {
1694       self = "NO-SUCH-CGI!";
1695    }
1696
1697    /* List available unhidden CGI's and export as "other-cgis" */
1698    for (d = cgi_dispatchers; d->name; d++)
1699    {
1700       if (d->description && strcmp(d->name, self))
1701       {
1702          string_append(&result, "<li><a href=\"" CGI_PREFIX);
1703          string_append(&result, d->name);
1704          string_append(&result, "\">");
1705          string_append(&result, d->description);
1706          string_append(&result, "</a></li>\n");
1707       }
1708    }
1709
1710    return result;
1711 }
1712
1713
1714 /*********************************************************************
1715  *
1716  * Function    :  dump_map
1717  *
1718  * Description :  HTML-dump a map for debugging
1719  *
1720  * Parameters  :
1721  *          1  :  the_map = map to dump
1722  *
1723  * Returns     :  string with HTML
1724  *
1725  *********************************************************************/
1726 char *dump_map(const struct map *the_map)
1727 {
1728    struct map_entry *cur_entry;
1729    char *ret = strdup("");
1730
1731    string_append(&ret, "<table>\n");
1732
1733    for (cur_entry = the_map->first;
1734         (cur_entry != NULL) && (ret != NULL);
1735         cur_entry = cur_entry->next)
1736    {
1737       string_append(&ret, "<tr><td><b>");
1738       string_join  (&ret, html_encode(cur_entry->name));
1739       string_append(&ret, "</b></td><td>");
1740       string_join  (&ret, html_encode(cur_entry->value));
1741       string_append(&ret, "</td></tr>\n");
1742    }
1743
1744    string_append(&ret, "</table>\n");
1745    return ret;
1746 }
1747
1748
1749 /*
1750   Local Variables:
1751   tab-width: 3
1752   end:
1753 */