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