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