1 const char cgi_rcs[] = "$Id: cgi.c,v 1.17 2001/08/05 15:57:38 oes Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
6 * Purpose : Declares functions to intercept request, generate
7 * html or gif answers, and to compose HTTP resonses.
9 * Functions declared include:
12 * Copyright : Written by and Copyright (C) 2001 the SourceForge
13 * IJBSWA team. http://ijbswa.sourceforge.net
15 * Based on the Internet Junkbuster originally written
16 * by and Copyright (C) 1997 Anonymous Coders and
17 * Junkbusters Corporation. http://www.junkbusters.com
19 * This program is free software; you can redistribute it
20 * and/or modify it under the terms of the GNU General
21 * Public License as published by the Free Software
22 * Foundation; either version 2 of the License, or (at
23 * your option) any later version.
25 * This program is distributed in the hope that it will
26 * be useful, but WITHOUT ANY WARRANTY; without even the
27 * implied warranty of MERCHANTABILITY or FITNESS FOR A
28 * PARTICULAR PURPOSE. See the GNU General Public
29 * License for more details.
31 * The GNU General Public License should be included with
32 * this file. If not, you can view it at
33 * http://www.gnu.org/copyleft/gpl.html
34 * or write to the Free Software Foundation, Inc., 59
35 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39 * Revision 1.17 2001/08/05 15:57:38 oes
40 * Adapted finish_http_response to new list_to_text
42 * Revision 1.16 2001/08/01 21:33:18 jongfoster
43 * Changes to fill_template() that reduce memory usage without having
44 * an impact on performance. I also renamed some variables so as not
45 * to clash with the C++ keywords "new" and "template".
47 * Revision 1.15 2001/08/01 21:19:22 jongfoster
48 * Moving file version information to a separate CGI page.
50 * Revision 1.14 2001/08/01 00:19:03 jongfoster
51 * New function: map_conditional() for an if-then-else syntax.
52 * Changing to use new version of show_defines()
54 * Revision 1.13 2001/07/30 22:08:36 jongfoster
55 * Tidying up #defines:
56 * - All feature #defines are now of the form FEATURE_xxx
57 * - Permanently turned off WIN_GUI_EDIT
58 * - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
60 * Revision 1.12 2001/07/29 18:47:05 jongfoster
61 * Adding missing #include "loadcfg.h"
63 * Revision 1.11 2001/07/18 17:24:37 oes
64 * Changed to conform to new pcrs interface
66 * Revision 1.10 2001/07/13 13:53:13 oes
67 * Removed all #ifdef PCRS and related code
69 * Revision 1.9 2001/06/29 21:45:41 oes
70 * Indentation, CRLF->LF, Tab-> Space
72 * Revision 1.8 2001/06/29 13:21:46 oes
73 * - Cosmetics: renamed and reordered functions, variables,
74 * texts, improved comments etc
76 * - Removed ij_untrusted_url() The relevant
77 * info is now part of the "untrusted" page,
78 * which is generated by filters.c:trust_url()
80 * - Generators of content now call finish_http_response()
81 * themselves, making jcc.c:chat() a little less
84 * - Removed obsolete "Pragma: no-cache" from our headers
86 * - http_responses now know their head length
88 * - fill_template now uses the new interface to pcrs, so that
89 * - long jobs (like whole files) no longer have to be assembled
90 * in a fixed size buffer
91 * - the new T (trivial) option is used, and the replacement may
92 * contain Perl syntax backrefs without confusing pcrs
94 * - Introduced default_exports() which generates a set of exports
95 * common to all CGIs and other content generators
97 * - Introduced convenience function map_block_killer()
99 * - Introduced convenience function make_menu()
101 * - Introduced CGI-like function error_response() which generates
102 * the "No such domain" and "Connect failed" messages using the
105 * - cgi_show_url_info:
106 * - adapted to new CGI features
107 * - form and answers now generated from same template
108 * - http:// prefix in URL now OK
111 * - adapted to new CGI features
112 * - no longer uses csp->init_proxy_args
115 * - moved menu generation to make_menu()
117 * - add_stats now writes single export map entries instead
120 * - Moved redirect_url() to filters.c
122 * - Fixed mem leak in free_http_response(), map_block_killer(),
124 * - Removed logentry from cancelled commit
126 * Revision 1.7 2001/06/09 10:51:58 jongfoster
127 * Changing "show URL info" handler to new style.
128 * Changing BUFSIZ ==> BUFFER_SIZE
130 * Revision 1.6 2001/06/07 23:05:19 jongfoster
131 * Removing code related to old forward and ACL files.
133 * Revision 1.5 2001/06/05 19:59:16 jongfoster
134 * Fixing multiline character string (a GCC-only "feature"), and snprintf (it's _snprintf under VC++).
136 * Revision 1.4 2001/06/04 10:41:52 swa
137 * show version string of cgi.h and cgi.c
139 * Revision 1.3 2001/06/03 19:12:16 oes
140 * introduced new cgi handling
142 * No revisions before 1.3
144 **********************************************************************/
150 #include <sys/types.h>
156 #define snprintf _snprintf
157 #endif /* def _WIN32 */
168 #include "miscutil.h"
169 #include "showargs.h"
172 const char cgi_h_rcs[] = CGI_H_VERSION;
174 const struct cgi_dispatcher cgi_dispatcher[] = {
177 "Show information about the current configuration" },
179 13, cgi_show_url_info,
180 "Show which actions apply to a URL and why" },
182 12, cgi_show_version,
183 "Show the source code version numbers" },
186 "HIDE Send the transparent or \"Junkbuster\" gif" },
189 "Junkbuster main page" },
190 { NULL, 0, NULL, NULL }
194 /*********************************************************************
196 * Function : dispatch_cgi
198 * Description : Checks if a request URL has either the magical hostname
199 * i.j.b or matches HOME_PAGE_URL/config/. If so, it parses
200 * the (rest of the) path as a cgi name plus query string,
201 * prepares a map that maps CGI parameter names to their values,
202 * initializes the http_response struct, and calls the
203 * relevant CGI handler function.
206 * 1 : csp = Current client state (buffers, headers, etc...)
208 * Returns : http_response if match, NULL if nonmatch or handler fail
210 *********************************************************************/
211 struct http_response *dispatch_cgi(struct client_state *csp)
213 char *argstring = NULL;
214 const struct cgi_dispatcher *d;
215 struct map *param_list;
216 struct http_response *rsp;
219 * Should we intercept ?
222 /* Either the host matches CGI_PREFIX_HOST ..*/
223 if (0 == strcmpic(csp->http->host, CGI_PREFIX_HOST))
225 /* ..then the path will all be for us */
226 argstring = csp->http->path;
228 /* Or it's the host part HOME_PAGE_URL, and the path /config ? */
229 else if ( (0 == strcmpic(csp->http->host, HOME_PAGE_URL + 7 ))
230 && (0 == strncmpic(csp->http->path,"/config", 7))
231 && ((csp->http->path[7] == '/') || (csp->http->path[7] == '\0')))
233 /* then it's everything following "/config" */
234 argstring = csp->http->path + 7;
242 * This is a CGI call.
245 /* Get mem for response or fail*/
246 if (NULL == ( rsp = zalloc(sizeof(*rsp))))
251 /* Remove leading slash */
252 if (*argstring == '/')
257 log_error(LOG_LEVEL_GPC, "%s%s cgi call", csp->http->hostport, csp->http->path);
258 log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 3",
259 csp->ip_addr_str, csp->http->cmd);
261 /* Find and start the right CGI function*/
262 for (d = cgi_dispatcher; d->handler; d++)
264 if (strncmp(argstring, d->name, d->name_length) == 0)
266 param_list = parse_cgi_parameters(argstring + d->name_length);
267 if ((d->handler)(csp, rsp, param_list))
272 free_map(param_list);
273 return(finish_http_response(rsp));
277 /* Can't get here, since cgi_default will match all requests */
284 /*********************************************************************
286 * Function : parse_cgi_parameters
288 * Description : Parse a URL-encoded argument string into name/value
289 * pairs and store them in a struct map list.
292 * 1 : string = string to be parsed
294 * Returns : pointer to param list
296 *********************************************************************/
297 struct map *parse_cgi_parameters(char *argstring)
300 char *vector[BUFFER_SIZE];
302 struct map *cgi_params = new_map();
304 if(*argstring == '?')
308 tmp = strdup(argstring);
310 pairs = ssplit(tmp, "&", vector, SZ(vector), 1, 1);
312 for (i = 0; i < pairs; i++)
314 if ((NULL != (p = strchr(vector[i], '='))) && (*(p+1) != '\0'))
317 map(cgi_params, url_decode(vector[i]), 0, url_decode(++p), 0);
327 /*********************************************************************
329 * Function : cgi_default
331 * Description : CGI function that is called if no action was given.
332 * Lists menu of available unhidden CGIs.
335 * 1 : csp = Current client state (buffers, headers, etc...)
336 * 2 : rsp = http_response data structure for output
337 * 3 : parameters = map of cgi parameters
341 *********************************************************************/
342 int cgi_default(struct client_state *csp, struct http_response *rsp,
343 struct map *parameters)
347 struct map * exports = default_exports(csp, "");
349 /* If there were other parameters, export a dump as "cgi-parameters" */
352 p = dump_map(parameters);
353 tmp = strsav(tmp, "<p>What made you think this cgi takes parameters?\n"
354 "Anyway, here they are, in case you're interested:</p>\n");
355 tmp = strsav(tmp, p);
356 map(exports, "cgi-parameters", 1, tmp, 0);
361 map(exports, "cgi-parameters", 1, "", 1);
364 rsp->body = fill_template(csp, "default", exports);
371 /*********************************************************************
373 * Function : cgi_send_banner
375 * Description : CGI function that returns a banner.
378 * 1 : csp = Current client state (buffers, headers, etc...)
379 * 2 : rsp = http_response data structure for output
380 * 3 : parameters = map of cgi parameters
383 * type : Selects the type of banner between "trans" and "jb".
384 * Defaults to "jb" if absent or != "trans".
388 *********************************************************************/
389 int cgi_send_banner(struct client_state *csp, struct http_response *rsp,
390 struct map *parameters)
392 if(strcmp(lookup(parameters, "type"), "trans"))
394 rsp->body = bindup(JBGIF, sizeof(JBGIF) - 1);
395 rsp->content_length = sizeof(JBGIF) - 1;
399 rsp->body = bindup(BLANKGIF, sizeof(BLANKGIF) - 1);
400 rsp->content_length = sizeof(BLANKGIF) - 1;
403 enlist(rsp->headers, "Content-Type: image/gif");
410 /*********************************************************************
412 * Function : cgi_show_version
414 * Description : CGI function that returns a a web page describing the
415 * file versions of IJB.
418 * 1 : csp = Current client state (buffers, headers, etc...)
419 * 2 : rsp = http_response data structure for output
420 * 3 : parameters = map of cgi parameters
423 * type : Selects the type of banner between "trans" and "jb".
424 * Defaults to "jb" if absent or != "trans".
428 *********************************************************************/
429 int cgi_show_version(struct client_state *csp, struct http_response *rsp,
430 struct map *parameters)
432 struct map * exports = default_exports(csp, "show-version");
434 map(exports, "sourceversions", 1, show_rcs(), 0);
436 rsp->body = fill_template(csp, "show-version", exports);
443 /*********************************************************************
445 * Function : cgi_show_status
447 * Description : CGI function that returns a a web page describing the
448 * current status of IJB.
451 * 1 : csp = Current client state (buffers, headers, etc...)
452 * 2 : rsp = http_response data structure for output
453 * 3 : parameters = map of cgi parameters
456 * type : Selects the type of banner between "trans" and "jb".
457 * Defaults to "jb" if absent or != "trans".
461 *********************************************************************/
462 int cgi_show_status(struct client_state *csp, struct http_response *rsp,
463 struct map *parameters)
469 char buf[BUFFER_SIZE];
471 const char * filename = NULL;
472 char * file_description = NULL;
474 struct map * exports = default_exports(csp, "show-status");
476 switch (*(lookup(parameters, "file")))
479 if (csp->actions_list)
481 filename = csp->actions_list->filename;
482 file_description = "Actions List";
489 filename = csp->rlist->filename;
490 file_description = "Regex Filter List";
498 filename = csp->tlist->filename;
499 file_description = "Trust List";
502 #endif /* def FEATURE_TRUST */
505 if (NULL != filename)
507 map(exports, "file-description", 1, file_description, 1);
508 map(exports, "filepath", 1, html_encode(filename), 0);
510 if ((fp = fopen(filename, "r")) == NULL)
512 map(exports, "content", 1, "<h1>ERROR OPENING FILE!</h1>", 1);
516 while (fgets(buf, sizeof(buf), fp))
518 p = html_encode(buf);
523 s = strsav(s, "<br>");
527 map(exports, "contents", 1, s, 0);
529 rsp->body = fill_template(csp, "show-status-file", exports);
535 map(exports, "redirect-url", 1, REDIRECT_URL, 1);
538 for (i=0; i < Argc; i++)
540 s = strsav(s, Argv[i]);
543 map(exports, "invocation", 1, s, 0);
545 map(exports, "options", 1, csp->config->proxy_args, 1);
546 show_defines(exports);
548 #ifdef FEATURE_STATISTICS
550 #else /* ndef FEATURE_STATISTICS */
551 map_block_killer(exports, "statistics");
552 #endif /* ndef FEATURE_STATISTICS */
554 if (csp->actions_list)
556 map(exports, "actions-filename", 1, csp->actions_list->filename, 1);
560 map(exports, "actions-filename", 1, "None specified", 1);
565 map(exports, "re-filter-filename", 1, csp->rlist->filename, 1);
569 map(exports, "re-filter-filename", 1, "None specified", 1);
575 map(exports, "trust-filename", 1, csp->tlist->filename, 1);
579 map(exports, "trust-filename", 1, "None specified", 1);
582 map_block_killer(exports, "trust-support");
583 #endif /* ndef FEATURE_TRUST */
585 rsp->body = fill_template(csp, "show-status", exports);
592 /*********************************************************************
594 * Function : cgi_show_url_info
596 * Description : CGI function that determines and shows which actions
597 * junkbuster will perform for a given url, and which
598 * matches starting from the defaults have lead to that.
601 * 1 : csp = Current client state (buffers, headers, etc...)
602 * 2 : rsp = http_response data structure for output
603 * 3 : parameters = map of cgi parameters
606 * url : The url whose actions are to be determined.
607 * If url is unset, the url-given conditional will be
608 * set, so that all but the form can be suppressed in
613 *********************************************************************/
614 int cgi_show_url_info(struct client_state *csp, struct http_response *rsp,
615 struct map *parameters)
619 struct map * exports = default_exports(csp, "show-url-info");
621 if (NULL == (url_param = strdup(lookup(parameters, "url"))) || *url_param == '\0')
623 map_block_killer(exports, "url-given");
624 map(exports, "url", 1, "", 1);
628 char *matches = NULL;
633 struct file_list *fl;
634 struct url_actions *b;
635 struct url_spec url[1];
636 struct current_action_spec action[1];
639 host += (strncmp(url_param, "http://", 7)) ? 0 : 7;
641 map(exports, "url", 1, host, 1);
642 map(exports, "url-html", 1, html_encode(host), 0);
644 init_current_action(action);
646 s = current_action_to_text(action);
647 map(exports, "default", 1, s , 0);
649 if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
651 map(exports, "matches", 1, "none" , 1);
652 map(exports, "final", 1, lookup(exports, "default"), 1);
655 free_current_action(action);
657 rsp->body = fill_template(csp, "show-url-info", exports);
663 s = strchr(host, '/');
673 s = strchr(host, ':');
683 /* if splitting the domain fails, punt */
684 if (url->dbuf == NULL)
686 map(exports, "matches", 1, "none" , 1);
687 map(exports, "final", 1, lookup(exports, "default"), 1);
691 free_current_action(action);
693 rsp->body = fill_template(csp, "show-url-info", exports);
699 for (b = b->next; NULL != b; b = b->next)
701 if ((b->url->port == 0) || (b->url->port == port))
703 if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
705 if ((b->url->path == NULL) ||
707 (regexec(b->url->preg, path, 0, NULL, 0) == 0)
709 (strncmp(b->url->path, path, b->url->pathlen) == 0)
713 s = actions_to_text(b->action);
714 matches = strsav(matches, "<b>{");
715 matches = strsav(matches, s);
716 matches = strsav(matches, " }</b><br>\n<code>");
717 matches = strsav(matches, b->url->spec);
718 matches = strsav(matches, "</code><br>\n<br>\n");
721 merge_current_action(action, b->action);
730 map(exports, "matches", 1, matches , 0);
734 map(exports, "matches", 1, "none", 1);
744 s = current_action_to_text(action);
745 map(exports, "final", 1, s, 0);
748 free_current_action(action);
751 rsp->body = fill_template(csp, "show-url-info", exports);
758 /*********************************************************************
760 * Function : error_response
762 * Description : returns an http_response that explains the reason
763 * why a request failed.
766 * 1 : csp = Current client state (buffers, headers, etc...)
767 * 2 : templatename = Which template should be used for the answer
768 * 3 : errno = system error number
770 * Returns : NULL if no memory, else http_response
772 *********************************************************************/
773 struct http_response *error_response(struct client_state *csp, const char *templatename, int err)
775 struct http_response *rsp;
776 struct map * exports = default_exports(csp, NULL);
778 if (NULL == ( rsp = (struct http_response *)zalloc(sizeof(*rsp))))
783 map(exports, "host-html", 1, html_encode(csp->http->host), 0);
784 map(exports, "hostport", 1, csp->http->hostport, 1);
785 map(exports, "hostport-html", 1, html_encode(csp->http->hostport), 0);
786 map(exports, "path", 1, csp->http->path, 1);
787 map(exports, "path-html", 1, html_encode(csp->http->path), 0);
788 map(exports, "error", 1, safe_strerror(err), 0);
789 map(exports, "host-ip", 1, csp->http->host_ip_addr_str, 1);
791 rsp->body = fill_template(csp, templatename, exports);
794 if (!strcmp(templatename, "no-such-domain"))
796 rsp->status = strdup("404 No such domain");
798 else if (!strcmp(templatename, "connect-failed"))
800 rsp->status = strdup("503 Connect failed");
803 return(finish_http_response(rsp));
807 /*********************************************************************
809 * Function : finish_http_response
811 * Description : Fill in the missing headers in an http response,
812 * and flatten the headers to an http head.
815 * 1 : rsp = pointer to http_response to be processed
817 * Returns : http_response, or NULL on failiure
819 *********************************************************************/
820 struct http_response *finish_http_response(struct http_response *rsp)
822 char buf[BUFFER_SIZE];
825 * Fill in the HTTP Status
827 sprintf(buf, "HTTP/1.0 %s", rsp->status ? rsp->status : "200 OK");
828 enlist_first(rsp->headers, buf);
831 * Set the Content-Length
833 if (rsp->content_length == 0)
835 rsp->content_length = rsp->body ? strlen(rsp->body) : 0;
837 sprintf(buf, "Content-Length: %d", rsp->content_length);
838 enlist(rsp->headers, buf);
841 * Fill in the default headers FIXME: Are these correct? sequence OK? check rfc!
842 * FIXME: Should have:
843 * "JunkBuster" GIF: Last-Modified: any *fixed* date in the past (as now).
844 * Expires: 5 minutes after the time when reply sent
845 * CGI, "blocked", & all other requests:
846 * Last-Modified: Time when reply sent
847 * Expires: Time when reply sent
848 * "Cache-Control: no-cache"
850 * See http://www.w3.org/Protocols/rfc2068/rfc2068
852 enlist_unique(rsp->headers, "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT", 14);
853 enlist_unique(rsp->headers, "Expires: Thu Jul 31, 1997 07:42:22 pm GMT", 8);
854 enlist_unique(rsp->headers, "Content-Type: text/html", 13);
860 if (NULL == (rsp->head = list_to_text(rsp->headers)))
862 free_http_response(rsp);
865 rsp->head_length = strlen(rsp->head);
872 /*********************************************************************
874 * Function : free_http_response
876 * Description : Free the memory occupied by an http_response
877 * and its depandant structures.
880 * 1 : rsp = pointer to http_response to be freed
884 *********************************************************************/
885 void free_http_response(struct http_response *rsp)
892 destroy_list(rsp->headers);
899 /*********************************************************************
901 * Function : fill_template
903 * Description : CGI support function that loads a given HTML
904 * template from the confdir, and fills it in
905 * by replacing @name@ with value using pcrs,
906 * for each item in the output map.
909 * 1 : csp = Current client state (buffers, headers, etc...)
910 * 3 : template = name of the HTML template to be used
911 * 2 : exports = map with fill in symbol -> name pairs
913 * Returns : char * with filled out form, or NULL if failiure
915 *********************************************************************/
916 char *fill_template(struct client_state *csp, const char *templatename, struct map *exports)
920 char buf[BUFFER_SIZE];
921 char *tmp_out_buffer;
922 char *file_buffer = NULL;
929 * Open template file or fail
931 snprintf(buf, BUFFER_SIZE, "%s/templates/%s", csp->config->confdir, templatename);
933 if(NULL == (fp = fopen(buf, "r")))
935 log_error(LOG_LEVEL_ERROR, "error loading template %s: %E", buf);
941 * Read the file, ignoring comments
943 while (fgets(buf, BUFFER_SIZE, fp))
945 /* skip lines starting with '#' */
946 if(*buf == '#') continue;
948 file_buffer = strsav(file_buffer, buf);
956 size = strlen(file_buffer) + 1;
959 * Assemble pcrs joblist from exports map
961 for (m = exports->first; m != NULL; m = m->next)
963 /* Enclose name in @@ */
964 snprintf(buf, BUFFER_SIZE, "@%s@", m->name);
966 /* Make and run job. */
967 job = pcrs_compile(buf, m->value, "sigTU", &error);
970 log_error(LOG_LEVEL_ERROR, "Error compiling template fill job %s: %d", m->name, error);
974 pcrs_execute(job, file_buffer, size, &tmp_out_buffer, &size);
975 if (file_buffer != tmp_out_buffer)
978 file_buffer = tmp_out_buffer;
993 /*********************************************************************
995 * Function : default_exports
997 * Description : returns a struct map list that contains exports
998 * which are common to all CGI functions.
1001 * 1 : exports = Structure to write output to. This
1002 * structure should be newly allocated and will be
1004 * 1 : csp = Current client state (buffers, headers, etc...)
1005 * 2 : caller = name of CGI who calls us and which should
1006 * be excluded from the generated menu.
1007 * Returns : NULL if no memory, else map
1009 *********************************************************************/
1010 struct map * default_exports(const struct client_state *csp, const char *caller)
1013 struct map * exports = new_map();
1015 map(exports, "version", 1, VERSION, 1);
1016 map(exports, "my-ip-address", 1, csp->my_ip_addr_str ? csp->my_ip_addr_str : "unknown", 1);
1017 map(exports, "my-hostname", 1, csp->my_hostname ? csp->my_hostname : "unknown", 1);
1018 map(exports, "admin-address", 1, csp->config->admin_address ? csp->config->admin_address : "fill@me.in.please", 1);
1019 map(exports, "homepage", 1, HOME_PAGE_URL, 1);
1020 map(exports, "default-cgi", 1, HOME_PAGE_URL "/config", 1);
1021 map(exports, "menu", 1, make_menu(caller), 0);
1022 map(exports, "code-status", 1, CODE_STATUS, 1);
1024 snprintf(buf, 20, "%d", csp->config->hport);
1025 map(exports, "my-port", 1, buf, 1);
1027 if(!strcmp(CODE_STATUS, "stable"))
1029 map_block_killer(exports, "unstable");
1032 if(csp->config->proxy_info_url != NULL)
1034 map(exports, "proxy-info-url", 1, csp->config->proxy_info_url, 1);
1038 map_block_killer(exports, "have-proxy-info");
1045 /*********************************************************************
1047 * Function : map_block_killer
1049 * Description : Convenience function.
1050 * Adds a "killer" for the conditional HTML-template
1051 * block <name>, i.e. a substitution of the regex
1052 * "if-<name>-start.*if-<name>-end" to the given
1056 * 1 : exports = map to extend
1057 * 2 : name = name of conditional block
1059 * Returns : extended map
1061 *********************************************************************/
1062 void map_block_killer(struct map *exports, const char *name)
1064 char buf[1000]; /* Will do, since the names are hardwired */
1066 snprintf(buf, 1000, "if-%s-start.*if-%s-end", name, name);
1067 map(exports, buf, 1, "", 1);
1071 /*********************************************************************
1073 * Function : map_conditional
1075 * Description : Convenience function.
1076 * Adds an "if-then-else" for the conditional HTML-template
1077 * block <name>, i.e. a substitution of the form:
1084 * The control structure and one of the alternatives
1088 * 1 : exports = map to extend
1089 * 2 : name = name of conditional block
1090 * 3 : choose_first = nonzero for first, zero for second.
1092 * Returns : extended map
1094 *********************************************************************/
1095 void map_conditional(struct map *exports, const char *name, int choose_first)
1097 char buf[1000]; /* Will do, since the names are hardwired */
1099 snprintf(buf, 1000, (choose_first
1100 ? "else-not-%s@.*@endif-%s"
1101 : "if-%s-then@.*@else-not-%s"),
1103 map(exports, buf, 1, "", 1);
1105 snprintf(buf, 1000, (choose_first ? "if-%s-then" : "endif-%s"), name);
1106 map(exports, buf, 1, "", 1);
1110 /*********************************************************************
1112 * Function : make_menu
1114 * Description : Returns an HTML-formatted menu of the available
1115 * unhidden CGIs, excluding the one given in <self>.
1117 * Parameters : self = name of CGI to leave out, can be NULL
1119 * Returns : menu string
1121 *********************************************************************/
1122 char *make_menu(const char *self)
1124 const struct cgi_dispatcher *d;
1125 char buf[BUFFER_SIZE];
1126 char *result = NULL;
1130 self = "NO-SUCH-CGI!";
1133 /* List available unhidden CGI's and export as "other-cgis" */
1134 for (d = cgi_dispatcher; d->handler; d++)
1136 if (strncmp(d->description, "HIDE", 4) && strcmp(d->name, self))
1138 snprintf(buf, BUFFER_SIZE, "<li><a href=%s/config/%s>%s</a></li>\n",
1139 HOME_PAGE_URL, d->name, d->description);
1140 result = strsav(result, buf);
1148 /*********************************************************************
1150 * Function : dump_map
1152 * Description : HTML-dump a map for debugging
1155 * 1 : the_map = map to dump
1157 * Returns : string with HTML
1159 *********************************************************************/
1160 char *dump_map(const struct map *the_map)
1162 struct map_entry *cur_entry = the_map->first;
1165 ret = strsav(ret, "<table>\n");
1169 ret = strsav(ret, "<tr><td><b>");
1170 ret = strsav(ret, cur_entry->name);
1171 ret = strsav(ret, "</b></td><td>");
1172 ret = strsav(ret, cur_entry->value);
1173 ret = strsav(ret, "</td></tr>\n");
1174 cur_entry = cur_entry->next;
1177 ret = strsav(ret, "</table>\n");
1183 #ifdef FEATURE_STATISTICS
1184 /*********************************************************************
1186 * Function : add_stats
1188 * Description : Add the blocking statistics to a given map.
1191 * 1 : exports = map to write to.
1193 * Returns : pointer to extended map
1195 *********************************************************************/
1196 struct map *add_stats(struct map *exports)
1198 float perc_rej; /* Percentage of http requests rejected */
1200 int local_urls_read = urls_read;
1201 int local_urls_rejected = urls_rejected;
1204 * Need to alter the stats not to include the fetch of this
1207 * Can't do following thread safely! doh!
1210 * urls_rejected--; * This will be incremented subsequently *
1213 if (local_urls_read == 0)
1215 map_block_killer(exports, "have-stats");
1219 map_block_killer(exports, "have-no-stats");
1221 perc_rej = (float)local_urls_rejected * 100.0F /
1222 (float)local_urls_read;
1224 sprintf(buf, "%d", local_urls_read);
1225 map(exports, "requests-received", 1, buf, 1);
1227 sprintf(buf, "%d", local_urls_rejected);
1228 map(exports, "requests-blocked", 1, buf, 1);
1230 sprintf(buf, "%6.2f", perc_rej);
1231 map(exports, "percent-blocked", 1, buf, 1);
1237 #endif /* def FEATURE_STATISTICS */