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