Changing "show URL info" handler to new style.
[privoxy.git] / filters.c
1 const char filters_rcs[] = "$Id: filters.c,v 1.15 2001/06/03 19:12:00 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_actions', `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.15  2001/06/03 19:12:00  oes
42  *    extracted-CGI relevant stuff
43  *
44  *    Revision 1.15  2001/06/03 11:03:48  oes
45  *    Makefile/in
46  *
47  *    introduced cgi.c
48  *
49  *    actions.c:
50  *
51  *    adapted to new enlist_unique arg format
52  *
53  *    conf loadcfg.c
54  *
55  *    introduced confdir option
56  *
57  *    filters.c filtrers.h
58  *
59  *     extracted-CGI relevant stuff
60  *
61  *    jbsockets.c
62  *
63  *     filled comment
64  *
65  *    jcc.c
66  *
67  *     support for new cgi mechansim
68  *
69  *    list.c list.h
70  *
71  *    functions for new list type: "map"
72  *    extended enlist_unique
73  *
74  *    miscutil.c .h
75  *    introduced bindup()
76  *
77  *    parsers.c parsers.h
78  *
79  *    deleted const struct interceptors
80  *
81  *    pcrs.c
82  *    added FIXME
83  *
84  *    project.h
85  *
86  *    added struct map
87  *    added struct http_response
88  *    changes struct interceptors to struct cgi_dispatcher
89  *    moved HTML stuff to cgi.h
90  *
91  *    re_filterfile:
92  *
93  *    changed
94  *
95  *    showargs.c
96  *    NO TIME LEFT
97  *
98  *    Revision 1.14  2001/06/01 10:30:55  oes
99  *    Added optional left-anchoring to domaincmp
100  *
101  *    Revision 1.13  2001/05/31 21:21:30  jongfoster
102  *    Permissionsfile / actions file changes:
103  *    - Changed "permission" to "action" throughout
104  *    - changes to file format to allow string parameters
105  *    - Moved helper functions to actions.c
106  *
107  *    Revision 1.12  2001/05/31 17:35:20  oes
108  *
109  *     - Enhanced domain part globbing with infix and prefix asterisk
110  *       matching and optional unanchored operation
111  *
112  *    Revision 1.11  2001/05/29 11:53:23  oes
113  *    "See why" link added to "blocked" page
114  *
115  *    Revision 1.10  2001/05/29 09:50:24  jongfoster
116  *    Unified blocklist/imagelist/permissionslist.
117  *    File format is still under discussion, but the internal changes
118  *    are (mostly) done.
119  *
120  *    Also modified interceptor behaviour:
121  *    - We now intercept all URLs beginning with one of the following
122  *      prefixes (and *only* these prefixes):
123  *        * http://i.j.b/
124  *        * http://ijbswa.sf.net/config/
125  *        * http://ijbswa.sourceforge.net/config/
126  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
127  *    - Internal changes so that intercepted and fast redirect pages
128  *      are not replaced with an image.
129  *    - Interceptors now have the option to send a binary page direct
130  *      to the client. (i.e. ijb-send-banner uses this)
131  *    - Implemented show-url-info interceptor.  (Which is why I needed
132  *      the above interceptors changes - a typical URL is
133  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
134  *      The previous mechanism would not have intercepted that, and
135  *      if it had been intercepted then it then it would have replaced
136  *      it with an image.)
137  *
138  *    Revision 1.9  2001/05/27 22:17:04  oes
139  *
140  *    - re_process_buffer no longer writes the modified buffer
141  *      to the client, which was very ugly. It now returns the
142  *      buffer, which it is then written by chat.
143  *
144  *    - content_length now adjusts the Content-Length: header
145  *      for modified documents rather than crunch()ing it.
146  *      (Length info in csp->content_length, which is 0 for
147  *      unmodified documents)
148  *
149  *    - For this to work, sed() is called twice when filtering.
150  *
151  *    Revision 1.8  2001/05/26 17:13:28  jongfoster
152  *    Filled in a function comment.
153  *
154  *    Revision 1.7  2001/05/26 15:26:15  jongfoster
155  *    ACL feature now provides more security by immediately dropping
156  *    connections from untrusted hosts.
157  *
158  *    Revision 1.6  2001/05/26 00:28:36  jongfoster
159  *    Automatic reloading of config file.
160  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
161  *    Most of the global variables have been moved to a new
162  *    struct configuration_spec, accessed through csp->config->globalname
163  *    Most of the globals remaining are used by the Win32 GUI.
164  *
165  *    Revision 1.5  2001/05/25 22:34:30  jongfoster
166  *    Hard tabs->Spaces
167  *
168  *    Revision 1.4  2001/05/22 18:46:04  oes
169  *
170  *    - Enabled filtering banners by size rather than URL
171  *      by adding patterns that replace all standard banner
172  *      sizes with the "Junkbuster" gif to the re_filterfile
173  *
174  *    - Enabled filtering WebBugs by providing a pattern
175  *      which kills all 1x1 images
176  *
177  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
178  *      which is selected by the (nonstandard and therefore
179  *      capital) letter 'U' in the option string.
180  *      It causes the quantifiers to be ungreedy by default.
181  *      Appending a ? turns back to greedy (!).
182  *
183  *    - Added a new interceptor ijb-send-banner, which
184  *      sends back the "Junkbuster" gif. Without imagelist or
185  *      MSIE detection support, or if tinygif = 1, or the
186  *      URL isn't recognized as an imageurl, a lame HTML
187  *      explanation is sent instead.
188  *
189  *    - Added new feature, which permits blocking remote
190  *      script redirects and firing back a local redirect
191  *      to the browser.
192  *      The feature is conditionally compiled, i.e. it
193  *      can be disabled with --disable-fast-redirects,
194  *      plus it must be activated by a "fast-redirects"
195  *      line in the config file, has its own log level
196  *      and of course wants to be displayed by show-proxy-args
197  *      Note: Boy, all the #ifdefs in 1001 locations and
198  *      all the fumbling with configure.in and acconfig.h
199  *      were *way* more work than the feature itself :-(
200  *
201  *    - Because a generic redirect template was needed for
202  *      this, tinygif = 3 now uses the same.
203  *
204  *    - Moved GIFs, and other static HTTP response templates
205  *      to project.h
206  *
207  *    - Some minor fixes
208  *
209  *    - Removed some >400 CRs again (Jon, you really worked
210  *      a lot! ;-)
211  *
212  *    Revision 1.3  2001/05/20 16:44:47  jongfoster
213  *    Removing last hardcoded JunkBusters.com URLs.
214  *
215  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
216  *    Version 2.9.4 checkin.
217  *    - Merged popupfile and cookiefile, and added control over PCRS
218  *      filtering, in new "permissionsfile".
219  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
220  *      file error you now get a message box (in the Win32 GUI) rather
221  *      than the program exiting with no explanation.
222  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
223  *      skipping.
224  *    - Removed tabs from "config"
225  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
226  *    - Bumped up version number.
227  *
228  *    Revision 1.1.1.1  2001/05/15 13:58:52  oes
229  *    Initial import of version 2.9.3 source tree
230  *
231  *
232  *********************************************************************/
233 \f
234
235 #include "config.h"
236
237 #include <stdio.h>
238 #include <sys/types.h>
239 #include <stdlib.h>
240 #include <ctype.h>
241 #include <string.h>
242
243 #ifndef _WIN32
244 #include <unistd.h>
245 #include <netinet/in.h>
246 #else
247 #include <winsock2.h>
248 #endif
249
250 #include "project.h"
251 #include "filters.h"
252 #include "encode.h"
253 #include "jcc.h"
254 #include "showargs.h"
255 #include "parsers.h"
256 #include "ssplit.h"
257 #include "gateway.h"
258 #include "jbsockets.h"
259 #include "errlog.h"
260 #include "jbsockets.h"
261 #include "miscutil.h"
262 #include "actions.h"
263 #include "cgi.h"
264
265 #ifdef _WIN32
266 #include "win32.h"
267 #endif
268
269 const char filters_h_rcs[] = FILTERS_H_VERSION;
270
271 /* Fix a problem with Solaris.  There should be no effect on other
272  * platforms.
273  * Solaris's isspace() is a macro which uses it's argument directly
274  * as an array index.  Therefore we need to make sure that high-bit
275  * characters generate +ve values, and ideally we also want to make
276  * the argument match the declared parameter type of "int".
277  */
278 #define ijb_isdigit(__X) isdigit((int)(unsigned char)(__X))
279
280
281 #ifdef ACL_FILES
282 /*********************************************************************
283  *
284  * Function    :  block_acl
285  *
286  * Description :  Block this request?
287  *                Decide yes or no based on ACL file.
288  *
289  * Parameters  :
290  *          1  :  dst = The proxy or gateway address this is going to.
291  *                      Or NULL to check all possible targets.
292  *          2  :  csp = Current client state (buffers, headers, etc...)
293  *                      Also includes the client IP address.
294  *
295  * Returns     : 0 = FALSE (don't block) and 1 = TRUE (do block)
296  *
297  *********************************************************************/
298 int block_acl(struct access_control_addr *dst,
299               struct client_state *csp)
300 {
301    struct access_control_list *acl = csp->config->acl;
302
303    /* if not using an access control list, then permit the connection */
304    if (acl == NULL)
305    {
306       return(0);
307    }
308
309    /* search the list */
310    while (acl != NULL)
311    {
312       if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
313       {
314          if (dst == NULL)
315          {
316             /* Just want to check if they have any access */
317             if (acl->action == ACL_PERMIT)
318             {
319                return(0);
320             }
321          }
322          else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
323            && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
324          {
325             if (acl->action == ACL_PERMIT)
326             {
327                return(0);
328             }
329             else
330             {
331                return(1);
332             }
333          }
334       }
335       acl = acl->next;
336    }
337
338    return(1);
339
340 }
341
342
343 /*********************************************************************
344  *
345  * Function    :  acl_addr
346  *
347  * Description :  Called from `load_aclfile' to parse an ACL address.
348  *
349  * Parameters  :
350  *          1  :  aspec = String specifying ACL address.
351  *          2  :  aca = struct access_control_addr to fill in.
352  *
353  * Returns     :  0 => Ok, everything else is an error.
354  *
355  *********************************************************************/
356 int acl_addr(char *aspec, struct access_control_addr *aca)
357 {
358    int i, masklength, port;
359    char *p;
360
361    masklength = 32;
362    port       =  0;
363
364    if ((p = strchr(aspec, '/')))
365    {
366       *p++ = '\0';
367
368       if (ijb_isdigit(*p) == 0)
369       {
370          return(-1);
371       }
372       masklength = atoi(p);
373    }
374
375    if ((masklength < 0) || (masklength > 32))
376    {
377       return(-1);
378    }
379
380    if ((p = strchr(aspec, ':')))
381    {
382       *p++ = '\0';
383
384       if (ijb_isdigit(*p) == 0)
385       {
386          return(-1);
387       }
388       port = atoi(p);
389    }
390
391    aca->port = port;
392
393    aca->addr = ntohl(resolve_hostname_to_ip(aspec));
394
395    if (aca->addr == -1)
396    {
397       log_error(LOG_LEVEL_ERROR, "can't resolve address for %s", aspec);
398       return(-1);
399    }
400
401    /* build the netmask */
402    aca->mask = 0;
403    for (i=1; i <= masklength ; i++)
404    {
405       aca->mask |= (1 << (32 - i));
406    }
407
408    /* now mask off the host portion of the ip address
409     * (i.e. save on the network portion of the address).
410     */
411    aca->addr = aca->addr & aca->mask;
412
413    return(0);
414
415 }
416 #endif /* def ACL_FILES */
417
418
419 /*********************************************************************
420  *
421  * Function    :  block_url
422  *
423  * Description :  Called from `chat'.  Check to see if we need to block this.
424  *
425  * Parameters  :
426  *          1  :  http = http_request request to "check" for blocked
427  *          2  :  csp = Current client state (buffers, headers, etc...)
428  *
429  * Returns     :  NULL => unblocked, else string to HTML block description.
430  *
431  *********************************************************************/
432 char *block_url(struct http_request *http, struct client_state *csp)
433 {
434    char *p;
435    int n;
436    int factor = 2;
437
438    if ((csp->action->flags & ACTION_BLOCK) == 0)
439    {
440       return(NULL);
441    }
442    else
443    {
444 #ifdef FORCE_LOAD
445       factor++;
446 #endif /* def FORCE_LOAD */
447
448       n  = strlen(CBLOCK);
449       n += factor * strlen(http->hostport);
450       n += factor * strlen(http->path);
451
452       p = (char *)malloc(n);
453
454 #ifdef FORCE_LOAD
455       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path,
456               http->hostport, http->path);
457 #else
458       sprintf(p, CBLOCK, http->hostport, http->path, http->hostport, http->path);
459 #endif /* def FORCE_LOAD */
460
461       return(p);
462    }
463 }
464
465
466 #ifdef IMAGE_BLOCKING
467 /*********************************************************************
468  *
469  * Function    :  block_imageurl
470  *
471  * Description :  Given a URL which is blocked, decide whether to 
472  *                send the "blocked" image or HTML.
473  *
474  * Parameters  :
475  *          1  :  http = URL to check.
476  *          2  :  csp = Current client state (buffers, headers, etc...)
477  *
478  * Returns     :  True (nonzero) if URL is in image list, false (0)
479  *                otherwise
480  *
481  *********************************************************************/
482 int block_imageurl(struct http_request *http, struct client_state *csp)
483 {
484 #ifdef DETECT_MSIE_IMAGES
485    if ((csp->accept_types 
486        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
487        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE))
488    {
489       return 1;
490    }
491    else if ((csp->accept_types 
492        & (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_IMAGE|ACCEPT_TYPE_MSIE_HTML))
493        == (ACCEPT_TYPE_IS_MSIE|ACCEPT_TYPE_MSIE_HTML))
494    {
495       return 0;
496    }
497 #endif
498
499    return ((csp->action->flags & ACTION_IMAGE) != 0);
500 }
501 #endif /* def IMAGE_BLOCKING */
502
503
504 #ifdef TRUST_FILES
505 /*********************************************************************
506  *
507  * Function    :  trust_url
508  *
509  * Description :  Should we "trust" this URL?  See "trustfile" line in config.
510  *
511  * Parameters  :
512  *          1  :  http = http_request request for requested URL
513  *          2  :  csp = Current client state (buffers, headers, etc...)
514  *
515  * Returns     :  NULL => trusted, else string to HTML "untrusted" description.
516  *
517  *********************************************************************/
518 char *trust_url(struct http_request *http, struct client_state *csp)
519 {
520    struct file_list *fl;
521    struct block_spec *b;
522    struct url_spec url[1], **tl, *t;
523    char *p, *h;
524    char *hostport, *path, *refer;
525    struct http_request rhttp[1];
526    int n;
527
528    if (((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL))
529    {
530       return(NULL);
531    }
532
533    *url = dsplit(http->host);
534
535    /* if splitting the domain fails, punt */
536    if (url->dbuf == NULL) return(NULL);
537
538    memset(rhttp, '\0', sizeof(*rhttp));
539
540    for (b = b->next; b ; b = b->next)
541    {
542       if ((b->url->port == 0) || (b->url->port == http->port))
543       {
544          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
545          {
546             if ((b->url->path == NULL) ||
547 #ifdef REGEX
548                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
549 #else
550                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
551 #endif
552             )
553             {
554                freez(url->dbuf);
555                freez(url->dvec);
556
557                if (b->reject == 0) return(NULL);
558
559                hostport = url_encode(http->hostport);
560                path     = url_encode(http->path);
561
562                if (csp->referrer)
563                {
564                   refer = url_encode(csp->referrer);
565                }
566                else
567                {
568                   refer = url_encode("undefined");
569                }
570
571                n  = strlen(CTRUST);
572                n += strlen(hostport);
573                n += strlen(path);
574                n += strlen(refer);
575
576                p = (char *)malloc(n);
577
578                sprintf(p, CTRUST, hostport, path, refer);
579
580                freez(hostport);
581                freez(path);
582                freez(refer);
583
584                return(p);
585             }
586          }
587       }
588    }
589
590    freez(url->dbuf);
591    freez(url->dvec);
592
593    if ((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9))
594    {
595       /* no referrer was supplied */
596       goto trust_url_not_trusted;
597    }
598
599    /* forge a URL from the referrer so we can use
600     * convert_url() to parse it into its components.
601     */
602
603    p = NULL;
604    p = strsav(p, "GET ");
605    p = strsav(p, csp->referrer + 9);   /* skip over "Referer: " */
606    p = strsav(p, " HTTP/1.0");
607
608    parse_http_request(p, rhttp, csp);
609
610    if (rhttp->cmd == NULL)
611    {
612       freez(p);
613       goto trust_url_not_trusted;
614    }
615
616    freez(p);
617
618    *url = dsplit(rhttp->host);
619
620    /* if splitting the domain fails, punt */
621    if (url->dbuf == NULL) goto trust_url_not_trusted;
622
623    for (tl = csp->config->trust_list; (t = *tl) ; tl++)
624    {
625       if ((t->port == 0) || (t->port == rhttp->port))
626       {
627          if ((t->domain[0] == '\0') || domaincmp(t, url) == 0)
628          {
629             if ((t->path == NULL) ||
630 #ifdef REGEX
631                (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
632 #else
633                (strncmp(t->path, rhttp->path, t->pathlen) == 0)
634 #endif
635             )
636             {
637                /* if the URL's referrer is from a trusted referrer, then
638                 * add the target spec to the trustfile as an unblocked
639                 * domain and return NULL (which means it's OK).
640                 */
641
642                FILE *fp;
643
644                freez(url->dbuf);
645                freez(url->dvec);
646
647                if ((fp = fopen(csp->config->trustfile, "a")))
648                {
649                   h = NULL;
650
651                   h = strsav(h, "~");
652                   h = strsav(h, http->hostport);
653
654                   p = http->path;
655                   if ((*p++ == '/')
656                   && (*p++ == '~'))
657                   {
658                   /* since this path points into a user's home space
659                    * be sure to include this spec in the trustfile.
660                    */
661                      if ((p = strchr(p, '/')))
662                      {
663                         *p = '\0';
664                         h = strsav(h, http->path);
665                         h = strsav(h, "/");
666                      }
667                   }
668
669                   free_http_request(rhttp);
670
671                   fprintf(fp, "%s\n", h);
672                   freez(h);
673                   fclose(fp);
674                }
675                return(NULL);
676             }
677          }
678       }
679    }
680
681 trust_url_not_trusted:
682    free_http_request(rhttp);
683
684    hostport = url_encode(http->hostport);
685    path     = url_encode(http->path);
686
687    if (csp->referrer)
688    {
689       refer = url_encode(csp->referrer);
690    }
691    else
692    {
693       refer = url_encode("undefined");
694    }
695
696    n  = strlen(CTRUST);
697    n += strlen(hostport);
698    n += strlen(path);
699    n += strlen(refer);
700
701    p = (char *)malloc(n);
702    sprintf(p, CTRUST, hostport, path, refer);
703
704    freez(hostport);
705    freez(path);
706    freez(refer);
707
708    return(p);
709
710 }
711 #endif /* def TRUST_FILES */
712
713
714 #ifdef PCRS
715 /*********************************************************************
716  *
717  * Function    :  re_process_buffer
718  *
719  * Description :  Apply all jobs from the joblist (aka. Perl regexp's) to
720  *                the text buffer that's been accumulated in csp->iob->buf
721  *                and set csp->content_length to the modified size.
722  *
723  * Parameters  :
724  *          1  :  csp = Current client state (buffers, headers, etc...)
725  *
726  * Returns     :  a pointer to the (newly allocated) modified buffer.
727  *                
728  *
729  *********************************************************************/
730 char *re_process_buffer(struct client_state *csp)
731 {
732    int hits=0;
733    int size = csp->iob->eod - csp->iob->cur;
734    char *old=csp->iob->cur, *new = NULL;
735    pcrs_job *job, *joblist;
736
737    struct file_list *fl;
738    struct re_filterfile_spec *b;
739
740    /* Sanity first ;-) */
741    if (size <= 0)
742    {
743       return(strdup(""));
744    }
745
746    if ( ( NULL == (fl = csp->rlist) ) || ( NULL == (b = fl->f) ) )
747    {
748       log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering.");
749       return(strdup(""));
750    }
751
752    joblist = b->joblist;
753
754
755    log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) ...",
756               csp->http->hostport, csp->http->path, size);
757
758    /* Apply all jobs from the joblist */
759    for (job = joblist; NULL != job; job = job->next)
760    {
761       hits += pcrs_exec_substitution(job, old, size, &new, &size);
762       if (old != csp->iob->cur) free(old);
763       old=new;
764    }
765
766    log_error(LOG_LEVEL_RE_FILTER, " produced %d hits (new size %d).", hits, size);
767
768    csp->content_length = size;
769
770    /* fwiw, reset the iob */
771    IOB_RESET(csp);
772    return(new);
773
774 }
775 #endif /* def PCRS */
776
777
778 /*********************************************************************
779  *
780  * Function    :  url_actions
781  *
782  * Description :  Gets the actions for this URL.
783  *
784  * Parameters  :
785  *          1  :  http = http_request request for blocked URLs
786  *          2  :  csp = Current client state (buffers, headers, etc...)
787  *
788  * Returns     :  N/A
789  *
790  *********************************************************************/
791 void url_actions(struct http_request *http, 
792                  struct client_state *csp)
793 {
794    struct file_list *fl;
795    struct url_actions *b;
796
797    init_current_action(csp->action);
798
799    if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL))
800    {
801       return;
802    }
803
804    apply_url_actions(csp->action, http, b);
805 }
806
807
808 /*********************************************************************
809  *
810  * Function    :  apply_url_actions
811  *
812  * Description :  Applies a list of URL actions.
813  *
814  * Parameters  :
815  *          1  :  action = Destination.
816  *          2  :  http = Current URL
817  *          3  :  b = list of URL actions to apply
818  *
819  * Returns     :  N/A
820  *
821  *********************************************************************/
822 void apply_url_actions(struct current_action_spec *action, 
823                        struct http_request *http, 
824                        struct url_actions *b)
825 {
826    struct url_spec url[1];
827
828    if (b == NULL)
829    {
830       /* Should never happen */
831       return;
832    }
833
834    *url = dsplit(http->host);
835
836    /* if splitting the domain fails, punt */
837    if (url->dbuf == NULL)
838    {
839       return;
840    }
841
842    for (b = b->next; NULL != b; b = b->next)
843    {
844       if ((b->url->port == 0) || (b->url->port == http->port))
845       {
846          if ((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0))
847          {
848             if ((b->url->path == NULL) ||
849 #ifdef REGEX
850                (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
851 #else
852                (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
853 #endif
854             )
855             {
856                merge_current_action(action, b->action);
857             }
858          }
859       }
860    }
861
862    freez(url->dbuf);
863    freez(url->dvec);
864 }
865
866
867 /*********************************************************************
868  *
869  * Function    :  forward_url
870  *
871  * Description :  Should we forward this to another proxy?
872  *
873  * Parameters  :
874  *          1  :  http = http_request request for current URL
875  *          2  :  csp = Current client state (buffers, headers, etc...)
876  *
877  * Returns     :  Pointer to forwarding information.
878  *
879  *********************************************************************/
880 const struct forward_spec * forward_url(struct http_request *http,
881                                         struct client_state *csp)
882 {
883    static const struct forward_spec fwd_default[1] = { 0 }; /* All zeroes */
884    struct forward_spec *fwd = csp->config->forward;
885    struct url_spec url[1];
886
887    if (fwd == NULL)
888    {
889       return(fwd_default);
890    }
891
892    *url = dsplit(http->host);
893
894    /* if splitting the domain fails, punt */
895    if (url->dbuf == NULL)
896    {
897       return(fwd_default);
898    }
899
900    while (fwd != NULL)
901    {
902       if ((fwd->url->port == 0) || (fwd->url->port == http->port))
903       {
904          if ((fwd->url->domain[0] == '\0') || (domaincmp(fwd->url, url) == 0))
905          {
906             if ((fwd->url->path == NULL) ||
907 #ifdef REGEX
908                (regexec(fwd->url->preg, http->path, 0, NULL, 0) == 0)
909 #else
910                (strncmp(fwd->url->path, http->path, fwd->url->pathlen) == 0)
911 #endif
912             )
913             {
914                freez(url->dbuf);
915                freez(url->dvec);
916                return(fwd);
917             }
918          }
919       }
920       fwd = fwd->next;
921    }
922
923    freez(url->dbuf);
924    freez(url->dvec);
925    return(fwd_default);
926
927 }
928
929
930 /*********************************************************************
931  *
932  * Function    :  dsplit
933  *
934  * Description :  Takes a domain and returns a pointer to a url_spec
935  *                structure populated with dbuf, dcnt and dvec.  The
936  *                other fields in the structure that is returned are zero.
937  *
938  * Parameters  :
939  *          1  :  domain = a URL address
940  *
941  * Returns     :  url_spec structure populated with dbuf, dcnt and dvec.
942  *
943  *********************************************************************/
944 struct url_spec dsplit(char *domain)
945 {
946    struct url_spec ret[1];
947    char *v[BUFSIZ];
948    int size;
949    char *p;
950
951    memset(ret, '\0', sizeof(*ret));
952
953    if (domain[strlen(domain) - 1] == '.')
954    {
955           ret->unanchored |= ANCHOR_RIGHT;
956         }
957         if (domain[0] == '.')
958    {
959           ret->unanchored |= ANCHOR_LEFT;
960         }
961
962    ret->dbuf = strdup(domain);
963
964    /* map to lower case */
965    for (p = ret->dbuf; *p ; p++) *p = tolower(*p);
966
967    /* split the domain name into components */
968    ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
969
970    if (ret->dcnt <= 0)
971    {
972       memset(ret, '\0', sizeof(ret));
973       return(*ret);
974    }
975
976    /* save a copy of the pointers in dvec */
977    size = ret->dcnt * sizeof(*ret->dvec);
978
979    if ((ret->dvec = (char **)malloc(size)))
980    {
981       memcpy(ret->dvec, v, size);
982    }
983
984
985    return(*ret);
986
987 }
988
989
990 /*********************************************************************
991  *
992  * Function    :  simple_domaincmp
993  *
994  * Description :  Domain-wise Compare fqdn's.  The comparison is 
995  *                both left- and right-anchored.  The individual
996  *                domain names are compared with trivialmatch().
997  *                This is only used by domaincmp.
998  *
999  * Parameters  :
1000  *          1  :  pv = array of patterns to compare
1001  *          2  :  fv = array of domain components to compare
1002  *          3  :  len = length of the arrays (both arrays are the
1003  *                      same length - if they weren't, it couldn't
1004  *                      possibly be a match).
1005  *
1006  * Returns     :  0 => domains are equivalent, else no match.
1007  *
1008  *********************************************************************/
1009 static int simple_domaincmp(char **pv, char **fv, int len)
1010 {
1011    int n;
1012
1013    for (n = 0; n < len; n++)
1014    {
1015       if (simplematch(pv[n], fv[n]))
1016       {
1017          return 1;
1018       }
1019    }
1020
1021    return 0;
1022 }
1023
1024
1025 /*********************************************************************
1026  *
1027  * Function    :  domaincmp
1028  *
1029  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
1030  *                pattern->unachored, the comparison is un-, left-,
1031  *                right-anchored, or both.
1032  *                The individual domain names are compared with
1033  *                trivialmatch().
1034  *
1035  * Parameters  :
1036  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
1037  *          2  :  fqdn = domain name against which the patterns are compared.
1038  *
1039  * Returns     :  0 => domains are equivalent, else no match.
1040  *
1041  *********************************************************************/
1042 int domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
1043 {
1044    char **pv, **fv;  /* vectors  */
1045    int    plen, flen;
1046    int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
1047
1048    plen = pattern->dcnt;
1049    flen = fqdn->dcnt;
1050
1051    if (flen < plen)
1052    {
1053       /* fqdn is too short to match this pattern */
1054       return 1;
1055    }
1056
1057    pv   = pattern->dvec;
1058    fv   = fqdn->dvec;
1059
1060    if (unanchored == ANCHOR_LEFT)
1061    {
1062       /*
1063        * Right anchored.
1064        *
1065        * Convert this into a fully anchored pattern with
1066        * the fqdn and pattern the same length
1067        */
1068       fv += (flen - plen); /* flen - plen >= 0 due to check above */
1069       return simple_domaincmp(pv, fv, plen);
1070    }
1071    else if (unanchored == 0)
1072    {
1073       /* Fully anchored, check length */
1074       if (flen != plen)
1075       {
1076          return 1;
1077       }
1078       return simple_domaincmp(pv, fv, plen);
1079    }
1080    else if (unanchored == ANCHOR_RIGHT)
1081    {
1082       /* Left anchored, ignore all extra in fqdn */
1083       return simple_domaincmp(pv, fv, plen);
1084    }
1085    else
1086    {
1087       /* Unanchored */
1088       int n;
1089       int maxn = flen - plen;
1090       for (n = 0; n <= maxn; n++)
1091       {
1092          if (!simple_domaincmp(pv, fv, plen))
1093          {
1094             return 0;
1095          }
1096          /*
1097           * Doesn't match from start of fqdn
1098           * Try skipping first part of fqdn
1099           */
1100          fv++;
1101       }
1102       return 1;
1103    }
1104 }
1105
1106
1107
1108 /*
1109   Local Variables:
1110   tab-width: 3
1111   end:
1112 */