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