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