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