Fatal errors loading configuration files now give better error messages.
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.6 2001/05/26 00:28:36 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
5  *
6  * Purpose     :  Declares functions to parse/crunch headers and pages.
7  *                Functions declared include:
8  *                   `acl_addr', `add_stats', `block_acl', `block_imageurl',
9  *                   `block_url', `url_permissions', `domaincmp', `dsplit',
10  *                   `filter_popups', `forward_url', 'redirect_url',
11  *                   `ij_untrusted_url', `intercept_url', `re_process_buffer',
12  *                   `show_proxy_args', 'ijb_send_banner', and `trust_url'
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: filters.c,v $
41  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
42  *    Automatic reloading of config file.
43  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
44  *    Most of the global variables have been moved to a new
45  *    struct configuration_spec, accessed through csp->config->globalname
46  *    Most of the globals remaining are used by the Win32 GUI.
47  *
48  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
49  *    Hard tabs->Spaces
50  *
51  *    Revision 1.4  2001/05/22 18:46:04  oes
52  *
53  *    - Enabled filtering banners by size rather than URL
54  *      by adding patterns that replace all standard banner
55  *      sizes with the "Junkbuster" gif to the re_filterfile
56  *
57  *    - Enabled filtering WebBugs by providing a pattern
58  *      which kills all 1x1 images
59  *
60  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
61  *      which is selected by the (nonstandard and therefore
62  *      capital) letter 'U' in the option string.
63  *      It causes the quantifiers to be ungreedy by default.
64  *      Appending a ? turns back to greedy (!).
65  *
66  *    - Added a new interceptor ijb-send-banner, which
67  *      sends back the "Junkbuster" gif. Without imagelist or
68  *      MSIE detection support, or if tinygif = 1, or the
69  *      URL isn't recognized as an imageurl, a lame HTML
70  *      explanation is sent instead.
71  *
72  *    - Added new feature, which permits blocking remote
73  *      script redirects and firing back a local redirect
74  *      to the browser.
75  *      The feature is conditionally compiled, i.e. it
76  *      can be disabled with --disable-fast-redirects,
77  *      plus it must be activated by a "fast-redirects"
78  *      line in the config file, has its own log level
79  *      and of course wants to be displayed by show-proxy-args
80  *      Note: Boy, all the #ifdefs in 1001 locations and
81  *      all the fumbling with configure.in and acconfig.h
82  *      were *way* more work than the feature itself :-(
83  *
84  *    - Because a generic redirect template was needed for
85  *      this, tinygif = 3 now uses the same.
86  *
87  *    - Moved GIFs, and other static HTTP response templates
88  *      to project.h
89  *
90  *    - Some minor fixes
91  *
92  *    - Removed some >400 CRs again (Jon, you really worked
93  *      a lot! ;-)
94  *
95  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
96  *    Removing last hardcoded JunkBusters.com URLs.
97  *
98  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
99  *    Version 2.9.4 checkin.
100  *    - Merged popupfile and cookiefile, and added control over PCRS
101  *      filtering, in new "permissionsfile".
102  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
103  *      file error you now get a message box (in the Win32 GUI) rather
104  *      than the program exiting with no explanation.
105  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
106  *      skipping.
107  *    - Removed tabs from "config"
108  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
109  *    - Bumped up version number.
110  *
111  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
112  *    Initial import of version 2.9.3 source tree
113  *
114  *
115  *********************************************************************/
116 \f
117
118 #include "config.h"
119
120 #include <stdio.h>
121 #include <sys/types.h>
122 #include <stdlib.h>
123 #include <ctype.h>
124 #include <string.h>
125
126 #ifndef _WIN32
127 #include <unistd.h>
128 #include <netinet/in.h>
129 #else
130 #include <winsock2.h>
131 #endif
132
133 #include "project.h"
134 #include "filters.h"
135 #include "encode.h"
136 #include "jcc.h"
137 #include "showargs.h"
138 #include "parsers.h"
139 #include "ssplit.h"
140 #include "gateway.h"
141 #include "jbsockets.h"
142 #include "errlog.h"
143 #include "jbsockets.h"
144
145 #ifdef _WIN32
146 #include "win32.h"
147 #endif
148
149 const char filters_h_rcs[] = FILTERS_H_VERSION;
150
151 /* Fix a problem with Solaris.  There should be no effect on other
152  * platforms.
153  * Solaris's isspace() is a macro which uses it's argument directly
154  * as an array index.  Therefore we need to make sure that high-bit
155  * characters generate +ve values, and ideally we also want to make
156  * the argument match the declared parameter type of "int".
157  */
158 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
159
160
161 static const char CBLOCK[] = 
162 #ifdef AMIGA 
163        "HTTP/1.0 403 Request for blocked URL\n" 
164 #else /* ifndef AMIGA */
165        "HTTP/1.0 202 Request for blocked URL\n"
166 #endif /* ndef AMIGA */
167        "Pragma: no-cache\n"
168        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
169        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
170        "Content-Type: text/html\n\n"
171        "<html>\n"
172        "<head>\n"
173        "<title>Internet Junkbuster: Request for blocked URL</title>\n"
174        "</head>\n"
175        WHITEBG
176        "<center><h1>"
177        BANNER
178        "</h1></center>\n"
179       "<p align=center>Your request for <b>%s%s</b><br>\n"
180       "was blocked because it matches the following pattern "
181       "in the blockfile: <b>%s</b>\n</p>"
182 #ifdef FORCE_LOAD
183        "<p align=center><a href=\"http://%s" FORCE_PREFIX
184         "%s\">Go there anyway.</a></p>"
185 #endif /* def FORCE_LOAD */
186       "</body>\n"
187       "</html>\n";
188
189 #ifdef TRUST_FILES
190 static const char CTRUST[] =
191 #ifdef AMIGA 
192        "HTTP/1.0 403 Request for untrusted URL\n"
193 #else /* ifndef AMIGA */
194        "HTTP/1.0 202 Request for untrusted URL\n"
195 #endif /* ndef AMIGA */
196        "Pragma: no-cache\n"
197        "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
198        "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
199        "Content-Type: text/html\n\n"
200        "<html>\n"
201        "<head>\n"
202        "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
203        "</head>\n"
204        WHITEBG
205        "<center>"
206        "<a href=http://internet.junkbuster.com/ij-untrusted-url?%s+%s+%s>"
207        BANNER
208        "</a>"
209        "</center>"
210        "</body>\n"
211        "</html>\n";
212 #endif /* def TRUST_FILES */
213
214
215 #ifdef ACL_FILES
216 /*********************************************************************
217  *
218  * Function    :  block_acl
219  *
220  * Description :  Block this request?
221  *                Decide yes or no based on ACL file.
222  *
223  * Parameters  :
224  *          1  :  dst = The proxy or gateway address this is going to.
225  *                      Or NULL to check all possible targets.
226  *          2  :  csp = Current client state (buffers, headers, etc...)
227  *                      Also includes the client IP address.
228  *
229  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
230  *
231  *********************************************************************/
232 int block_acl(struct access_control_addr *dst,
233               struct client_state *csp)
234 {
235    struct file_list *fl;
236    struct access_control_list *a, *acl;
237
238    /* if not using an access control list, then permit the connection */
239    if (((fl = csp->alist) == NULL) || 
240        ((acl = (struct access_control_list *) fl->f) == NULL))
241    {
242       return(0);
243    }
244
245    /* search the list */
246    for (a = acl->next ; a ; a = a->next)
247    {
248       if ((csp->ip_addr_long & a->src->mask) == a->src->addr)
249       {
250          if (dst == NULL)
251          {
252             /* Just want to check if they have any access */
253             if (a->action == ACL_PERMIT)
254             {
255                return(0);
256             }
257          }
258          else if ( ((dst->addr & a->dst->mask) == a->dst->addr)
259            && ((dst->port == a->dst->port) || (a->dst->port == 0)))
260          {
261             if (a->action == ACL_PERMIT)
262             {
263                return(0);
264             }
265             else
266             {
267                return(1);
268             }
269          }
270       }
271    }
272
273    return(1);
274
275 }
276
277
278 /*********************************************************************
279  *
280  * Function    :  acl_addr
281  *
282  * Description :  Called from `load_aclfile'.  FIXME: I can't say more.
283  *
284  * Parameters  :
285  *          1  :  aspec = (what?)
286  *          2  :  aca = (what?)
287  *
288  * Returns     :  0 => Ok, everything else is an error.
289  *
290  *********************************************************************/
291 int acl_addr(char *aspec, struct access_control_addr *aca)
292 {
293    int i, masklength, port;
294    char *p;
295
296    masklength = 32;
297    port       =  0;
298
299    if ((p = strchr(aspec, '/')))
300    {
301       *p++ = '\0';
302
303       if (ijb_isdigit(*p) == 0)
304       {
305          return(-1);
306       }
307       masklength = atoi(p);
308    }
309
310    if ((masklength < 0) || (masklength > 32))
311    {
312       return(-1);
313    }
314
315    if ((p = strchr(aspec, ':')))
316    {
317       *p++ = '\0';
318
319       if (ijb_isdigit(*p) == 0)
320       {
321          return(-1);
322       }
323       port = atoi(p);
324    }
325
326    aca->port = port;
327
328    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
329
330    if (aca->addr == -1)
331    {
332       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
333       return(-1);
334    }
335
336    /* build the netmask */
337    aca->mask = 0;
338    for (i=1; i <= masklength ; i++)
339    {
340       aca->mask |= (1 << (32 - i));
341    }
342
343    /* now mask off the host portion of the ip address
344     * (i.e. save on the network portion of the address).
345     */
346    aca->addr = aca->addr & aca->mask;
347
348    return(0);
349
350 }
351 #endif /* def ACL_FILES */
352
353
354 /*********************************************************************
355  *
356  * Function    :  block_url
357  *
358  * Description :  Called from `chat'.  Check to see if we need to block this.
359  *
360  * Parameters  :
361  *          1  :  http = http_request request to "check" for blocked
362  *          2  :  csp = Current client state (buffers, headers, etc...)
363  *
364  * Returns     :  NULL => unblocked, else string to HTML block description.
365  *
366  *********************************************************************/
367 char *block_url(struct http_request *http, struct client_state *csp)
368 {
369    struct file_list *fl;
370    struct block_spec *b;
371    struct url_spec url[1];
372    char *p;
373    int n;
374
375    if (((fl = csp->blist) == NULL) || ((b = fl->f) == NULL))
376    {
377       return(NULL);
378    }
379
380    *url = dsplit(http->host);
381
382    /* if splitting the domain fails, punt */
383    if (url->dbuf == NULL) return(NULL);
384
385    for (b = b->next; b ; b = b->next)
386    {
387       if ((b->url->port == 0) || (b->url->port == http->port))
388       {
389          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
390          {
391             if ((b->url->path == NULL) ||
392 #ifdef REGEX
393                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
394 #else
395                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
396 #endif
397             )
398             {
399                freez(url->dbuf);
400                freez(url->dvec);
401
402                if (b->reject == 0) return(NULL);
403
404                n  = strlen(CBLOCK);
405                n += strlen(http->hostport);
406                n += strlen(http->path);
407                n += strlen(b->url->spec);
408 #ifdef FORCE_LOAD
409                n += strlen(http->hostport);
410                n += strlen(http->path);
411 #endif /* def FORCE_LOAD */
412
413                p = (char *)malloc(n);
414
415 #ifdef FORCE_LOAD
416                sprintf(p, CBLOCK, http->hostport, http->path, b->url->spec, http->hostport, http->path);
417 #else
418                sprintf(p, CBLOCK, http->hostport, http->path, b->url->spec);
419 #endif /* def FORCE_LOAD */
420
421                return(p);
422             }
423          }
424       }
425    }
426    freez(url->dbuf);
427    freez(url->dvec);
428    return(NULL);
429
430 }
431
432
433 #if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
434 /*********************************************************************
435  *
436  * Function    :  block_imageurl
437  *
438  * Description :  Given a URL which is blocked, decide whether to 
439  *                send the "blocked" image or HTML.
440  *
441  * Parameters  :
442  *          1  :  http = URL to check.
443  *          2  :  csp = Current client state (buffers, headers, etc...)
444  *
445  * Returns     :  True (nonzero) if URL is in image list, false (0)
446  *                otherwise
447  *
448  *********************************************************************/
449 int block_imageurl(struct http_request *http, struct client_state *csp)
450 {
451 #ifdef DETECT_MSIE_IMAGES
452    if ((csp->accept_types 
453        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
454        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
455    {
456       return 1;
457    }
458    else if ((csp->accept_types 
459        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
460        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
461    {
462       return 0;
463    }
464 #endif
465
466 #if defined(USE_IMAGE_LIST)
467    return block_imageurl_using_imagelist(http, csp);
468 #else
469    /* Don't know - assume HTML */
470    return 0;
471 #endif
472 }
473 #endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
474
475
476 #ifdef USE_IMAGE_LIST
477 /*********************************************************************
478  *
479  * Function    :  block_imageurl
480  *
481  * Description :  Test if a URL is in the imagelist.
482  *
483  * Parameters  :
484  *          1  :  http = URL to check.
485  *          2  :  csp = Current client state (buffers, headers, etc...)
486  *
487  * Returns     :  True (nonzero) if URL is in image list, false (0)
488  *                otherwise
489  *
490  *********************************************************************/
491 int block_imageurl_using_imagelist(struct http_request *http, struct client_state *csp)
492 {
493    struct file_list *fl;
494    struct block_spec *b;
495    struct url_spec url[1];
496
497    if (((fl = csp->ilist) == NULL) || ((b  = fl->f) == NULL))
498    {
499       return(0);
500    }
501
502    *url = dsplit(http->host);
503
504    /* if splitting the domain fails, punt */
505    if (url->dbuf == NULL) return(0);
506
507    for (b = b->next; b ; b = b->next)
508    {
509
510       if ((b->url->port == 0) || (b->url->port == http->port))
511       {
512          /* port matches, check domain */
513          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
514          {
515             /* domain matches, check path */
516             if ((b->url->path == NULL) ||
517 #ifdef REGEX
518                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
519 #else
520                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
521 #endif
522             )
523             {
524                /* Matches */
525                freez(url->dbuf);
526                freez(url->dvec);
527
528                if (b->reject == 0) return(0);
529
530                return(1);
531             }
532          }
533       }
534    }
535    freez(url->dbuf);
536    freez(url->dvec);
537    return(0);
538
539 }
540 #endif /* def USE_IMAGE_LIST */
541
542
543 #ifdef PCRS
544 /*********************************************************************
545  *
546  * Function    :  re_process_buffer
547  *
548  * Description :  Apply all jobs from the joblist (aka. Perl regexp's) to
549  *                the text buffer that's been accumulated in csp->iob->buf.
550  *                Then, write the modified buffer out to the client
551  *                (Maybe this should happen from jcc.c via flush_socket
552  *                for better readability).
553  *
554  * Parameters  :
555  *          1  :  csp = Current client state (buffers, headers, etc...)
556  *
557  * Returns     :  N/A
558  *
559  *********************************************************************/
560 void re_process_buffer(struct client_state *csp)
561 {
562    int hits=0;
563    int size = csp->iob->eod - csp->iob->cur;
564    char *old=csp->iob->cur, *new = NULL;
565    pcrs_job *job, *joblist;
566
567    struct file_list *fl;
568    struct re_filterfile_spec *b;
569
570    /* Sanity first ;-) */
571    if (size <= 0)
572    {
573       return;
574    }
575
576    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
577    {
578       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
579       return;
580    }
581
582    joblist = b->joblist;
583
584
585    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
586               csp->http->hostport, csp->http->path, size);
587
588    /* Apply all jobs from the joblist */
589    for (job = joblist; NULL != job; job = job->next)
590    {
591       hits += pcrs_exec_substitution(job, old, size, &new, &size);
592       if (old != csp->iob->cur) free(old);
593       old=new;
594    }
595
596    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
597
598    if (write_socket(csp->cfd, old, size) != size)
599    {
600       log_error(LOG_LEVEL_ERROR, "write to client failed.");
601    }
602
603    /* fwiw, reset the iob */
604    IOB_RESET(csp);
605    freez(new);
606    return;
607
608 }
609 #endif /* def PCRS */
610
611
612 #ifdef TRUST_FILES
613 /*********************************************************************
614  *
615  * Function    :  trust_url
616  *
617  * Description :  Should we "trust" this URL?  See "trustfile" line in config.
618  *
619  * Parameters  :
620  *          1  :  http = http_request request for requested URL
621  *          2  :  csp = Current client state (buffers, headers, etc...)
622  *
623  * Returns     :  NULL => trusted, else string to HTML "untrusted" description.
624  *
625  *********************************************************************/
626 char *trust_url(struct http_request *http, struct client_state *csp)
627 {
628    struct file_list *fl;
629    struct block_spec *b;
630    struct url_spec url[1], **tl, *t;
631    char *p, *h;
632    char *hostport, *path, *refer;
633    struct http_request rhttp[1];
634    int n;
635
636    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
637    {
638       return(NULL);
639    }
640
641    *url = dsplit(http->host);
642
643    /* if splitting the domain fails, punt */
644    if (url->dbuf == NULL) return(NULL);
645
646    memset(rhttp, '\0', sizeof(*rhttp));
647
648    for (b = b->next; b ; b = b->next)
649    {
650       if ((b->url->port == 0) || (b->url->port == http->port))
651       {
652          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
653          {
654             if ((b->url->path == NULL) ||
655 #ifdef REGEX
656                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
657 #else
658                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
659 #endif
660             )
661             {
662                freez(url->dbuf);
663                freez(url->dvec);
664
665                if (b->reject == 0) return(NULL);
666
667                hostport = url_encode(http->hostport);
668                path     = url_encode(http->path);
669
670                if (csp->referrer)
671                {
672                   refer = url_encode(csp->referrer);
673                }
674                else
675                {
676                   refer = url_encode("undefined");
677                }
678
679                n  = strlen(CTRUST);
680                n += strlen(hostport);
681                n += strlen(path);
682                n += strlen(refer);
683
684                p = (char *)malloc(n);
685
686                sprintf(p, CTRUST, hostport, path, refer);
687
688                freez(hostport);
689                freez(path);
690                freez(refer);
691
692                return(p);
693             }
694          }
695       }
696    }
697
698    freez(url->dbuf);
699    freez(url->dvec);
700
701    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
702    {
703       /* no referrer was supplied */
704       goto trust_url_not_trusted;
705    }
706
707    /* forge a URL from the referrer so we can use
708     * convert_url() to parse it into its components.
709     */
710
711    p = NULL;
712    p = strsav(p, "GET ");
713    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
714    p = strsav(p, " HTTP/1.0");
715
716    parse_http_request(p, rhttp, csp);
717
718    if (rhttp->cmd == NULL)
719    {
720       freez(p);
721       goto trust_url_not_trusted;
722    }
723
724    freez(p);
725
726    *url = dsplit(rhttp->host);
727
728    /* if splitting the domain fails, punt */
729    if (url->dbuf == NULL) goto trust_url_not_trusted;
730
731    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
732    {
733       if ((t->port == 0) || (t->port == rhttp->port))
734       {
735          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
736          {
737             if ((t->path == NULL) ||
738 #ifdef REGEX
739                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
740 #else
741                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
742 #endif
743             )
744             {
745                /* if the URL's referrer is from a trusted referrer, then
746                 * add the target spec to the trustfile as an unblocked
747                 * domain and return NULL (which means it's OK).
748                 */
749
750                FILE *fp;
751
752                freez(url->dbuf);
753                freez(url->dvec);
754
755                if ((fp = fopen(csp->config->trustfile, "a")))
756                {
757                   h = NULL;
758
759                   h = strsav(h, "~");
760                   h = strsav(h, http->hostport);
761
762                   p = http->path;
763                   if ((*p++ == '/')
764                   && (*p++ == '~'))
765                   {
766                   /* since this path points into a user's home space
767                    * be sure to include this spec in the trustfile.
768                    */
769                      if ((p = strchr(p, '/')))
770                      {
771                         *p = '\0';
772                         h = strsav(h, http->path);
773                         h = strsav(h, "/");
774                      }
775                   }
776
777                   free_http_request(rhttp);
778
779                   fprintf(fp, "%s\n", h);
780                   freez(h);
781                   fclose(fp);
782                }
783                return(NULL);
784             }
785          }
786       }
787    }
788
789 trust_url_not_trusted:
790    free_http_request(rhttp);
791
792    hostport = url_encode(http->hostport);
793    path     = url_encode(http->path);
794
795    if (csp->referrer)
796    {
797       refer = url_encode(csp->referrer);
798    }
799    else
800    {
801       refer = url_encode("undefined");
802    }
803
804    n  = strlen(CTRUST);
805    n += strlen(hostport);
806    n += strlen(path);
807    n += strlen(refer);
808
809    p = (char *)malloc(n);
810    sprintf(p, CTRUST, hostport, path, refer);
811
812    freez(hostport);
813    freez(path);
814    freez(refer);
815
816    return(p);
817
818 }
819 #endif /* def TRUST_FILES */
820
821
822 /*********************************************************************
823  *
824  * Function    :  intercept_url
825  *
826  * Description :  checks the URL `basename' against a list of URLs to
827  *                snarf. If it matches, it calls the associated function
828  *                which returns an HTML page to send back to the client.
829  *                Right now, we snarf:
830  *                      "show-proxy-args", and
831  *                      "ij-untrusted-url" (optional w/TRUST_FILES)
832  *
833  * Parameters  :
834  *          1  :  http = http_request request, check `basename's of blocklist
835  *          2  :  csp = Current client state (buffers, headers, etc...)
836  *
837  * Returns     :  NULL for no recognized URLs, or an HTML description page.
838  *
839  *********************************************************************/
840 char *intercept_url(struct http_request *http, struct client_state *csp)
841 {
842    char *basename;
843    const struct interceptors *v;
844
845    basename = strrchr(http->path, '/');
846
847    if (basename == NULL) return(NULL);
848
849    basename ++; /* first char past the last slash */
850
851    if (*basename)
852    {
853       for (v = intercept_patterns; v->str; v++)
854       {
855          if (strncmp(basename, v->str, v->len) == 0)
856          {
857             return((v->interceptor)(http, csp));
858          }
859       }
860    }
861
862    return(NULL);
863
864 }
865
866 #ifdef FAST_REDIRECTS
867 /*********************************************************************
868  *
869  * Function    :  redirect_url
870  *
871  * Description :  Checks for redirection URLs and returns a HTTP redirect
872  *                to the destination URL.
873  *
874  * Parameters  :
875  *          1  :  http = http_request request, check `basename's of blocklist
876  *          2  :  csp = Current client state (buffers, headers, etc...)
877  *
878  * Returns     :  NULL if URL was clean, HTTP redirect otherwise.
879  *
880  *********************************************************************/
881 char *redirect_url(struct http_request *http, struct client_state *csp)
882 {
883    char *p, *q;
884
885    p = q = csp->http->path;
886    log_error(LOG_LEVEL_REDIRECTS, "checking path: %s", p);
887
888    /* find the last URL encoded in the request */
889    while (p = strstr(p, "http://"))
890    {
891       q = p++;
892    }
893
894    /* if there was any, generate and return a HTTP redirect */
895    if (q != csp->http->path)
896    {
897       log_error(LOG_LEVEL_REDIRECTS, "redirecting to: %s", q);
898
899       p = (char *)malloc(strlen(HTTP_REDIRECT_TEMPLATE) + strlen(q));
900       sprintf(p, HTTP_REDIRECT_TEMPLATE, q);
901       return(p);
902    }
903    else
904    {
905       return(NULL);
906    }
907
908 }
909 #endif /* def FAST_REDIRECTS */
910
911 /*********************************************************************
912  *
913  * Function    :  url_permissions
914  *
915  * Description :  Gets the permissions for this URL.
916  *
917  * Parameters  :
918  *          1  :  http = http_request request for blocked URLs
919  *          2  :  csp = Current client state (buffers, headers, etc...)
920  *
921  * Returns     :  permissions bitmask specifiying what this URL can do.
922  *                If not on list, will be default_permissions.
923  *
924  *********************************************************************/
925 int url_permissions(struct http_request *http, struct client_state *csp)
926 {
927    struct file_list *fl;
928    struct permissions_spec *b;
929    struct url_spec url[1];
930
931    if (((fl = csp->permissions_list) == NULL) || ((b = fl->f) == NULL))
932    {
933       return(csp->config->default_permissions);
934    }
935
936    *url = dsplit(http->host);
937
938    /* if splitting the domain fails, punt */
939    if (url->dbuf == NULL)
940    {
941       return(csp->config->default_permissions);
942    }
943
944    for (b = b->next; NULL != b; b = b->next)
945    {
946       if ((b->url->port == 0) || (b->url->port == http->port))
947       {
948          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
949          {
950             if ((b->url->path == NULL) ||
951 #ifdef REGEX
952                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
953 #else
954                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
955 #endif
956             )
957             {
958                freez(url->dbuf);
959                freez(url->dvec);
960                return(b->permissions);
961             }
962          }
963       }
964    }
965
966    freez(url->dbuf);
967    freez(url->dvec);
968    return(csp->config->default_permissions);
969
970 }
971
972
973 /*********************************************************************
974  *
975  * Function    :  forward_url
976  *
977  * Description :  Should we forward this to another proxy?
978  *
979  * Parameters  :
980  *          1  :  http = http_request request for current URL
981  *          2  :  csp = Current client state (buffers, headers, etc...)
982  *
983  * Returns     :  Return gw_default for no forward match,
984  *                else a gateway pointer to a specific forwarding proxy.
985  *
986  *********************************************************************/
987 const struct gateway *forward_url(struct http_request *http, struct client_state *csp)
988 {
989    struct file_list *fl;
990    struct forward_spec *b;
991    struct url_spec url[1];
992
993    if (((fl = csp->flist) == NULL) || ((b = fl->f) == NULL))
994    {
995       return(gw_default);
996    }
997
998    *url = dsplit(http->host);
999
1000    /* if splitting the domain fails, punt */
1001    if (url->dbuf == NULL) return(gw_default);
1002
1003    for (b = b->next; b ; b = b->next)
1004    {
1005       if ((b->url->port == 0) || (b->url->port == http->port))
1006       {
1007          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
1008          {
1009             if ((b->url->path == NULL) ||
1010 #ifdef REGEX
1011                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
1012 #else
1013                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
1014 #endif
1015             )
1016             {
1017                freez(url->dbuf);
1018                freez(url->dvec);
1019                return(b->gw);
1020             }
1021          }
1022       }
1023    }
1024
1025    freez(url->dbuf);
1026    freez(url->dvec);
1027    return(gw_default);
1028
1029 }
1030
1031
1032 /*********************************************************************
1033  *
1034  * Function    :  dsplit
1035  *
1036  * Description :  Takes a domain and returns a pointer to a url_spec
1037  *                structure populated with dbuf, dcnt and dvec.  The
1038  *                other fields in the structure that is returned are zero.
1039  *
1040  * Parameters  :
1041  *          1  :  domain = a URL address
1042  *
1043  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
1044  *
1045  *********************************************************************/
1046 struct url_spec dsplit(char *domain)
1047 {
1048    struct url_spec ret[1];
1049    char *v[BUFSIZ];
1050    int size;
1051    char *p;
1052
1053    memset(ret, '\0', sizeof(*ret));
1054
1055    if ((p = strrchr(domain, '.')))
1056    {
1057       if (*(++p) == '\0')
1058       {
1059          ret->toplevel = 1;
1060       }
1061    }
1062
1063    ret->dbuf = strdup(domain);
1064
1065    /* map to lower case */
1066    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
1067
1068    /* split the domain name into components */
1069    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
1070
1071    if (ret->dcnt <= 0)
1072    {
1073       memset(ret, '\0', sizeof(ret));
1074       return(*ret);
1075    }
1076
1077    /* save a copy of the pointers in dvec */
1078    size = ret->dcnt * sizeof(*ret->dvec);
1079
1080    if ((ret->dvec = (char **)malloc(size)))
1081    {
1082       memcpy(ret->dvec, v, size);
1083    }
1084
1085    return(*ret);
1086
1087 }
1088
1089
1090 /*********************************************************************
1091  *
1092  * Function    :  domaincmp
1093  *
1094  * Description :  Compare domain names.
1095  *                domaincmp("a.b.c" , "a.b.c")  => 0 (MATCH)
1096  *                domaincmp("a*.b.c", "a.b.c")  => 0 (MATCH)
1097  *                domaincmp("b.c"   , "a.b.c")  => 0 (MATCH)
1098  *                domaincmp(""      , "a.b.c")  => 0 (MATCH)
1099  *
1100  * Parameters  :
1101  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1102  *          2  :  fqdn = domain name against which the patterns are compared.
1103  *
1104  * Returns     :  0 => domains are equivalent, else no match.
1105  *
1106  *********************************************************************/
1107 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1108 {
1109    char **pv, **fv;  /* vectors  */
1110    int    pn,   fn;  /* counters */
1111    char  *p,   *f;   /* chars    */
1112
1113    pv = pattern->dvec;
1114    pn = pattern->dcnt;
1115
1116    fv = fqdn->dvec;
1117    fn = fqdn->dcnt;
1118
1119    while ((pn > 0) && (fn > 0))
1120    {
1121       p = pv[--pn];
1122       f = fv[--fn];
1123
1124       while (*p && *f && (*p == tolower(*f)))
1125       {
1126          p++, f++;
1127       }
1128
1129       if ((*p != tolower(*f)) && (*p != '*')) return(1);
1130    }
1131
1132    if (pn > 0) return(1);
1133
1134    return(0);
1135
1136 }
1137
1138
1139 /* intercept functions */
1140
1141 /*********************************************************************
1142  *
1143  * Function    :  show_proxy_args
1144  *
1145  * Description :  This "crunch"es "http:/any.thing/show-proxy-args" and
1146  *                returns a web page describing the current status of IJB.
1147  *
1148  * Parameters  :
1149  *          1  :  http = ignored
1150  *          2  :  csp = Current client state (buffers, headers, etc...)
1151  *
1152  * Returns     :  A string that contains the current status of IJB.
1153  *
1154  *********************************************************************/
1155 char *show_proxy_args(struct http_request *http, struct client_state *csp)
1156 {
1157    char *s = NULL;
1158
1159 #ifdef SPLIT_PROXY_ARGS
1160    FILE * fp;
1161    char buf[BUFSIZ];
1162    char * p;
1163    const char * filename = NULL;
1164    const char * file_description = NULL;
1165    char * query_string = strrchr(http->path, '?');
1166    char which_file = '\0';
1167
1168
1169    if (query_string != NULL)
1170    {
1171       /* first char past the last '?' (maybe '\0')*/
1172       which_file = query_string[1];
1173    }
1174    switch (which_file)
1175    {
1176    case 'b':
1177       if (csp->blist)
1178       {
1179          filename = csp->blist->filename;
1180          file_description = "Block List";
1181       }
1182       break;
1183    case 'p':
1184       if (csp->permissions_list)
1185       {
1186          filename = csp->permissions_list->filename;
1187          file_description = "Permissions List";
1188       }
1189       break;
1190    case 'f':
1191       if (csp->flist)
1192       {
1193          filename = csp->flist->filename;
1194          file_description = "Forward List";
1195       }
1196       break;
1197
1198 #ifdef ACL_FILES
1199    case 'a':
1200       if (csp->alist)
1201       {
1202          filename = csp->alist->filename;
1203          file_description = "Access Control List";
1204       }
1205       break;
1206 #endif /* def ACL_FILES */
1207
1208 #ifdef USE_IMAGE_LIST
1209    case 'i':
1210       if (csp->ilist)
1211       {
1212          filename = csp->ilist->filename;
1213          file_description = "Image List";
1214       }
1215       break;
1216 #endif /* def USE_IMAGE_LIST */
1217
1218 #ifdef PCRS
1219    case 'r':
1220       if (csp->rlist)
1221       {
1222          filename = csp->rlist->filename;
1223          file_description = "RE Filter List";
1224       }
1225       break;
1226 #endif /* def PCRS */
1227
1228 #ifdef TRUST_FILES
1229    case 't':
1230       if (csp->tlist)
1231       {
1232          filename = csp->tlist->filename;
1233          file_description = "Trust List";
1234       }
1235       break;
1236 #endif /* def TRUST_FILES */
1237    }
1238
1239    if (filename)
1240    {
1241       /* Display specified file */
1242       /* FIXME: Add HTTP headers so this isn't cached */
1243       s = strsav(s,
1244          "HTTP/1.0 200 OK\n"
1245          "Server: IJ/" VERSION "\n"
1246          "Content-type: text/html\n"
1247          "Pragma: no-cache\n"
1248          "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1249          "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1250          "\n"
1251
1252          "<html>"
1253          "<head>"
1254          "<title>Internet Junkbuster Proxy Status - ");
1255       s = strsav(s, file_description);
1256       s = strsav(s, 
1257          "</title>"
1258          "</head>\n"
1259          "<body bgcolor=\"#f8f8f0\" link=\"#000078\" alink=\"#ff0022\" vlink=\"#787878\">\n"
1260          "<center>\n"
1261          "<h1>" BANNER "\n");
1262       s = strsav(s, file_description);
1263       s = strsav(s, 
1264          "</h1></center>\n"
1265          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1266          "<h2>");
1267       s = strsav(s, file_description);
1268       s = strsav(s,
1269          "</h2>\n"
1270          "Contents of file &quot;<code>");
1271       p = html_encode(filename);
1272       s = strsav(s, p);
1273       freez(p);
1274       s = strsav(s,
1275          "</code>&quot;:<br>\n"
1276          "</p>\n"
1277          "<pre>");
1278       
1279       if ((fp = fopen(filename, "r")) == NULL)
1280       {
1281          s = strsav(s, "</pre><h1>ERROR OPENING FILE!</h1><pre>");
1282       }
1283       else
1284       {
1285          while (fgets(buf, sizeof(buf), fp))
1286          {
1287             p = html_encode(buf);
1288             if (p)
1289             {
1290                s = strsav(s, p);
1291                freez(p);
1292                s = strsav(s, "<br>");
1293             }
1294          }
1295          fclose(fp);
1296       }
1297
1298       s = strsav(s,
1299          "</pre>\n"
1300          "<br>\n"
1301          "<p><a href=\"show-proxy-args\">Back to proxy status</a></p>\n"
1302          "<br>\n"
1303          "<small><small><p>\n"
1304          "The " BANNER " Proxy - \n"
1305          "<a href=\"" HOME_PAGE_URL "\">" HOME_PAGE_URL "</a>\n"
1306          "</small></small>"
1307          "</body></html>\n");
1308       return(s);
1309    }
1310 #endif /* def SPLIT_PROXY_ARGS */
1311    
1312    s = strsav(s, csp->config->proxy_args->header);
1313    s = strsav(s, csp->config->proxy_args->invocation);
1314 #ifdef STATISTICS
1315    s = add_stats(s);
1316 #endif /* def STATISTICS */
1317    s = strsav(s, csp->config->proxy_args->gateways);
1318
1319 #ifdef SPLIT_PROXY_ARGS
1320    s = strsav(s, 
1321       "<h2>The following files are in use:</h2>\n"
1322       "<p>(Click a filename to view it)</p>\n"
1323       "<ul>\n");
1324
1325    if (csp->blist)
1326    {
1327       s = strsav(s, "<li>Block List: <a href=\"show-proxy-args?block\"><code>");
1328       s = strsav(s, csp->blist->filename);
1329       s = strsav(s, "</code></a></li>\n");
1330    }
1331
1332    if (csp->permissions_list)
1333    {
1334       s = strsav(s, "<li>Permissions List: <a href=\"show-proxy-args?permit\"><code>");
1335       s = strsav(s, csp->permissions_list->filename);
1336       s = strsav(s, "</code></a></li>\n");
1337    }
1338
1339    if (csp->flist)
1340    {
1341       s = strsav(s, "<li>Forward List: <a href=\"show-proxy-args?forward\"><code>");
1342       s = strsav(s, csp->flist->filename);
1343       s = strsav(s, "</code></a></li>\n");
1344    }
1345
1346 #ifdef ACL_FILES
1347    if (csp->alist)
1348    {
1349       s = strsav(s, "<li>Access Control List: <a href=\"show-proxy-args?acl\"><code>");
1350       s = strsav(s, csp->alist->filename);
1351       s = strsav(s, "</code></a></li>\n");
1352    }
1353 #endif /* def ACL_FILES */
1354
1355 #ifdef USE_IMAGE_LIST
1356    if (csp->ilist)
1357    {
1358       s = strsav(s, "<li>Image List: <a href=\"show-proxy-args?image\"><code>");
1359       s = strsav(s, csp->ilist->filename);
1360       s = strsav(s, "</code></a></li>\n");
1361    }
1362 #endif /* def USE_IMAGE_LIST */
1363
1364 #ifdef PCRS
1365    if (csp->rlist)
1366    {
1367       s = strsav(s, "<li>RE Filter List: <a href=\"show-proxy-args?re\"><code>");
1368       s = strsav(s, csp->rlist->filename);
1369       s = strsav(s, "</code></a></li>\n");
1370    }
1371 #endif /* def PCRS */
1372
1373 #ifdef TRUST_FILES
1374    if (csp->tlist)
1375    {
1376       s = strsav(s, "<li>Trust List: <a href=\"show-proxy-args?trust\"><code>");
1377       s = strsav(s, csp->tlist->filename);
1378       s = strsav(s, "</code></a></li>\n");
1379    }
1380 #endif /* def TRUST_FILES */
1381
1382    s = strsav(s, "</ul>");
1383
1384 #else /* ifndef SPLIT_PROXY_ARGS */
1385    if (csp->blist)
1386    {
1387       s = strsav(s, csp->blist->proxy_args);
1388    }
1389
1390    if (csp->clist)
1391    {
1392       s = strsav(s, csp->clist->proxy_args);
1393    }
1394
1395    if (csp->flist)
1396    {
1397       s = strsav(s, csp->flist->proxy_args);
1398    }
1399
1400 #ifdef ACL_FILES
1401    if (csp->alist)
1402    {
1403       s = strsav(s, csp->alist->proxy_args);
1404    }
1405 #endif /* def ACL_FILES */
1406
1407 #ifdef USE_IMAGE_LIST
1408    if (csp->ilist)
1409    {
1410       s = strsav(s, csp->ilist->proxy_args);
1411    }
1412 #endif /* def USE_IMAGE_LIST */
1413
1414 #ifdef PCRS
1415    if (csp->rlist)
1416    {
1417       s = strsav(s, csp->rlist->proxy_args);
1418    }
1419 #endif /* def PCRS */
1420
1421 #ifdef TRUST_FILES
1422    if (csp->tlist)
1423    {
1424       s = strsav(s, csp->tlist->proxy_args);
1425    }
1426 #endif /* def TRUST_FILES */
1427
1428 #endif /* ndef SPLIT_PROXY_ARGS */
1429
1430    s = strsav(s, csp->config->proxy_args->trailer);
1431
1432    return(s);
1433
1434 }
1435
1436
1437 /*********************************************************************
1438  *
1439  * Function    :  ijb_send_banner
1440  *
1441  * Description :  This "crunch"es "http:/any.thing/ijb-send-banner and
1442  *                thus triggers sending the image in jcc.c:chat.
1443  *                For the unlikely case, that the imagefile/MSIE
1444  *                mechanism is not used, or tinygif = 0, a page
1445  *                describing the reson of the interception is generated.
1446  *
1447  * Parameters  :
1448  *          1  :  http = http_request request for crunched URL
1449  *          2  :  csp = Current client state (buffers, headers, etc...)
1450  *
1451  * Returns     :  A string that contains why this was intercepted.
1452  *
1453  *********************************************************************/
1454 char *ijb_send_banner(struct http_request *http, struct client_state *csp)
1455 {
1456    return(strdup(CNOBANNER));
1457 }
1458
1459 #ifdef TRUST_FILES
1460 /*********************************************************************
1461  *
1462  * Function    :  ij_untrusted_url
1463  *
1464  * Description :  This "crunch"es "http:/any.thing/ij-untrusted-url" and
1465  *                returns a web page describing why it was untrusted.
1466  *
1467  * Parameters  :
1468  *          1  :  http = http_request request for crunched URL
1469  *          2  :  csp = Current client state (buffers, headers, etc...)
1470  *
1471  * Returns     :  A string that contains why this was untrusted.
1472  *
1473  *********************************************************************/
1474 char *ij_untrusted_url(struct http_request *http, struct client_state *csp)
1475 {
1476    int n;
1477    char *hostport, *path, *refer, *p, *v[9];
1478    char buf[BUFSIZ];
1479    struct url_spec **tl, *t;
1480
1481
1482    static const char format[] =
1483       "HTTP/1.0 200 OK\r\n"
1484       "Pragma: no-cache\n"
1485       "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
1486       "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
1487       "Content-Type: text/html\n\n"
1488       "<html>\n"
1489       "<head>\n"
1490       "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
1491       "</head>\n"
1492       BODY
1493       "<center><h1>"
1494       BANNER
1495       "</h1></center>"
1496       "The " BANNER " Proxy "
1497       "<A href=\"" HOME_PAGE_URL "\">"
1498       "(" HOME_PAGE_URL ") </A>"
1499       "intercepted the request for %s%s\n"
1500       "because the URL is not trusted.\n"
1501       "<br><br>\n";
1502
1503    if ((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4)
1504    {
1505       hostport = url_decode(v[1]);
1506       path     = url_decode(v[2]);
1507       refer    = url_decode(v[3]);
1508    }
1509    else
1510    {
1511       hostport = strdup("undefined_host");
1512       path     = strdup("/undefined_path");
1513       refer    = strdup("undefined");
1514    }
1515
1516    n  = sizeof(format);
1517    n += strlen(hostport);
1518    n += strlen(path    );
1519
1520    if ((p = (char *)malloc(n)))
1521    {
1522       sprintf(p, format, hostport, path);
1523    }
1524
1525    strsav(p, "The referrer in this request was <strong>");
1526    strsav(p, refer);
1527    strsav(p, "</strong><br>\n");
1528
1529    freez(hostport);
1530    freez(path    );
1531    freez(refer   );
1532
1533    p = strsav(p, "<h3>The following referrers are trusted</h3>\n");
1534
1535    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
1536    {
1537       sprintf(buf, "%s<br>\n", t->spec);
1538       p = strsav(p, buf);
1539    }
1540
1541    if (csp->config->trust_info->next)
1542    {
1543       struct list *l;
1544
1545       strcpy(buf,
1546          "<p>"
1547          "You can learn more about what this means "
1548          "and what you may be able to do about it by "
1549          "reading the following documents:<br>\n"
1550          "<ol>\n"
1551       );
1552
1553       p = strsav(p, buf);
1554
1555       for (l = csp->config->trust_info->next; l ; l = l->next)
1556       {
1557          sprintf(buf,
1558             "<li> <a href=%s>%s</a><br>\n",
1559                l->str, l->str);
1560          p = strsav(p, buf);
1561       }
1562
1563       p = strsav(p, "</ol>\n");
1564    }
1565
1566    p = strsav(p, "</body>\n" "</html>\n");
1567
1568    return(p);
1569
1570 }
1571 #endif /* def TRUST_FILES */
1572
1573
1574 #ifdef STATISTICS
1575 /*********************************************************************
1576  *
1577  * Function    :  add_stats
1578  *
1579  * Description :  Statistics function of JB.  Called by `show_proxy_args'.
1580  *
1581  * Parameters  :
1582  *          1  :  s = string that holds the proxy args description page
1583  *
1584  * Returns     :  A pointer to the descriptive status web page.
1585  *
1586  *********************************************************************/
1587 char *add_stats(char *s)
1588 {
1589    /*
1590     * Output details of the number of requests rejected and
1591     * accepted. This is switchable in the junkbuster config.
1592     * Does nothing if this option is not enabled.
1593     */
1594
1595    float perc_rej;   /* Percentage of http requests rejected */
1596    char out_str[81];
1597    int local_urls_read     = urls_read;
1598    int local_urls_rejected = urls_rejected;
1599
1600    /*
1601     * Need to alter the stats not to include the fetch of this
1602     * page.
1603     *
1604     * Can't do following thread safely! doh!
1605     *
1606     * urls_read--;
1607     * urls_rejected--; * This will be incremented subsequently *
1608     */
1609
1610    s = strsav(s,"<h2>Statistics for this " BANNER ":</h2>\n");
1611
1612    if (local_urls_read == 0)
1613    {
1614
1615       s = strsav(s,"No activity so far!\n");
1616
1617    }
1618    else
1619    {
1620
1621       perc_rej = (float)local_urls_rejected * 100.0F /
1622             (float)local_urls_read;
1623
1624       sprintf(out_str,
1625          "%d requests received, %d filtered "
1626          "(%6.2f %%).",
1627          local_urls_read, 
1628          local_urls_rejected, perc_rej);
1629
1630       s = strsav(s,out_str);
1631    }
1632
1633    return(s);
1634 }
1635 #endif /* def STATISTICS */
1636
1637
1638 /*
1639   Local Variables:
1640   tab-width: 3
1641   end:
1642 */