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