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