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