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