Fix typo: Default port for https URLs is 443, not 143.
[privoxy.git] / urlmatch.c
1 const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.10.2.5 2003/02/28 13:09:29 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/Attic/urlmatch.c,v $
5  *
6  * Purpose     :  Declares functions to match URLs against URL
7  *                patterns.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                Privoxy team. http://www.privoxy.org/
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: urlmatch.c,v $
36  *    Revision 1.10.2.5  2003/02/28 13:09:29  oes
37  *    Fixed a rare double free condition as per Bug #694713
38  *
39  *    Revision 1.10.2.4  2003/02/28 12:57:44  oes
40  *    Moved freeing of http request structure to its owner
41  *    as per Dan Price's observations in Bug #694713
42  *
43  *    Revision 1.10.2.3  2002/11/12 16:50:40  oes
44  *    Fixed memory leak in parse_http_request() reported by Oliver Stoeneberg. Fixes bug #637073
45  *
46  *    Revision 1.10.2.2  2002/09/25 14:53:15  oes
47  *    Added basic support for OPTIONS and TRACE HTTP methods:
48  *    parse_http_url now recognizes the "*" URI as well as
49  *    the OPTIONS and TRACE method keywords.
50  *
51  *    Revision 1.10.2.1  2002/06/06 19:06:44  jongfoster
52  *    Adding support for proprietary Microsoft WebDAV extensions
53  *
54  *    Revision 1.10  2002/05/12 21:40:37  jongfoster
55  *    - Removing some unused code
56  *
57  *    Revision 1.9  2002/04/04 00:36:36  gliptak
58  *    always use pcre for matching
59  *
60  *    Revision 1.8  2002/04/03 23:32:47  jongfoster
61  *    Fixing memory leak on error
62  *
63  *    Revision 1.7  2002/03/26 22:29:55  swa
64  *    we have a new homepage!
65  *
66  *    Revision 1.6  2002/03/24 13:25:43  swa
67  *    name change related issues
68  *
69  *    Revision 1.5  2002/03/13 00:27:05  jongfoster
70  *    Killing warnings
71  *
72  *    Revision 1.4  2002/03/07 03:46:17  oes
73  *    Fixed compiler warnings
74  *
75  *    Revision 1.3  2002/03/03 14:51:11  oes
76  *    Fixed CLF logging: Added ocmd member for client's request to struct http_request
77  *
78  *    Revision 1.2  2002/01/21 00:14:09  jongfoster
79  *    Correcting comment style
80  *    Fixing an uninitialized memory bug in create_url_spec()
81  *
82  *    Revision 1.1  2002/01/17 20:53:46  jongfoster
83  *    Moving all our URL and URL pattern parsing code to the same file - it
84  *    was scattered around in filters.c, loaders.c and parsers.c.
85  *
86  *    Providing a single, simple url_match(pattern,url) function - rather than
87  *    the 3-line match routine which was repeated all over the place.
88  *
89  *    Renaming free_url to free_url_spec, since it frees a struct url_spec.
90  *
91  *    Providing parse_http_url() so that URLs can be parsed without faking a
92  *    HTTP request line for parse_http_request() or repeating the parsing
93  *    code (both of which were techniques that were actually in use).
94  *
95  *    Standardizing that struct http_request is used to represent a URL, and
96  *    struct url_spec is used to represent a URL pattern.  (Before, URLs were
97  *    represented as seperate variables and a partially-filled-in url_spec).
98  *
99  *
100  *********************************************************************/
101 \f
102
103 #include "config.h"
104
105 #ifndef _WIN32
106 #include <stdio.h>
107 #include <sys/types.h>
108 #endif
109
110 #include <stdlib.h>
111 #include <ctype.h>
112 #include <assert.h>
113 #include <string.h>
114
115 #if !defined(_WIN32) && !defined(__OS2__)
116 #include <unistd.h>
117 #endif
118
119 #include "project.h"
120 #include "urlmatch.h"
121 #include "ssplit.h"
122 #include "miscutil.h"
123 #include "errlog.h"
124
125 const char urlmatch_h_rcs[] = URLMATCH_H_VERSION;
126
127
128 /*********************************************************************
129  *
130  * Function    :  free_http_request
131  *
132  * Description :  Freez a http_request structure
133  *
134  * Parameters  :
135  *          1  :  http = points to a http_request structure to free
136  *
137  * Returns     :  N/A
138  *
139  *********************************************************************/
140 void free_http_request(struct http_request *http)
141 {
142    assert(http);
143
144    freez(http->cmd);
145    freez(http->ocmd);
146    freez(http->gpc);
147    freez(http->host);
148    freez(http->url);
149    freez(http->hostport);
150    freez(http->path);
151    freez(http->ver);
152    freez(http->host_ip_addr_str);
153    freez(http->dbuffer);
154    freez(http->dvec);
155    http->dcount = 0;
156 }
157
158
159 /*********************************************************************
160  *
161  * Function    :  parse_http_url
162  *
163  * Description :  Parse out the host and port from the URL.  Find the
164  *                hostname & path, port (if ':'), and/or password (if '@')
165  *
166  * Parameters  :
167  *          1  :  url = URL (or is it URI?) to break down
168  *          2  :  http = pointer to the http structure to hold elements.
169  *                       Will be zeroed before use.  Note that this
170  *                       function sets the http->gpc and http->ver
171  *                       members to NULL.
172  *          3  :  csp = Current client state (buffers, headers, etc...)
173  *
174  * Returns     :  JB_ERR_OK on success
175  *                JB_ERR_MEMORY on out of memory
176  *                JB_ERR_PARSE on malformed command/URL
177  *                             or >100 domains deep.
178  *
179  *********************************************************************/
180 jb_err parse_http_url(const char * url,
181                       struct http_request *http,
182                       struct client_state *csp)
183 {
184    /*
185     * Zero out the results structure
186     */
187    memset(http, '\0', sizeof(*http));
188
189
190    /*
191     * Save our initial URL
192     */
193    http->url = strdup(url);
194    if (http->url == NULL)
195    {
196       return JB_ERR_MEMORY;
197    }
198
199
200    /*
201     * Check for * URI. If found, we're done.
202     */  
203    if (*http->url == '*')
204    {
205       http->path = strdup("*");
206       http->hostport = strdup("");
207       return JB_ERR_OK;
208    }
209
210
211    /*
212     * Split URL into protocol,hostport,path.
213     */
214    {
215       char *buf;
216       char *url_noproto;
217       char *url_path;
218
219       buf = strdup(url);
220       if (buf == NULL)
221       {
222          return JB_ERR_MEMORY;
223       }
224
225       /* Find the start of the URL in our scratch space */
226       url_noproto = buf;
227       if (strncmpic(url_noproto, "http://",  7) == 0)
228       {
229          url_noproto += 7;
230          http->ssl = 0;
231       }
232       else if (strncmpic(url_noproto, "https://", 8) == 0)
233       {
234          url_noproto += 8;
235          http->ssl = 1;
236       }
237       else
238       {
239          http->ssl = 0;
240       }
241
242       url_path = strchr(url_noproto, '/');
243       if (url_path != NULL)
244       {
245          /*
246           * Got a path.
247           *
248           * NOTE: The following line ignores the path for HTTPS URLS.
249           * This means that you get consistent behaviour if you type a
250           * https URL in and it's parsed by the function.  (When the
251           * URL is actually retrieved, SSL hides the path part).
252           */
253          http->path = strdup(http->ssl ? "/" : url_path);
254          *url_path = '\0';
255          http->hostport = strdup(url_noproto);
256       }
257       else
258       {
259          /*
260           * Repair broken HTTP requests that don't contain a path,
261           * or CONNECT requests
262           */
263          http->path = strdup("/");
264          http->hostport = strdup(url_noproto);
265       }
266
267       freez(buf);
268
269       if ( (http->path == NULL)
270         || (http->hostport == NULL))
271       {
272          return JB_ERR_MEMORY;
273       }
274    }
275
276
277    /*
278     * Split hostport into user/password (ignored), host, port.
279     */
280    {
281       char *buf;
282       char *host;
283       char *port;
284
285       buf = strdup(http->hostport);
286       if (buf == NULL)
287       {
288          return JB_ERR_MEMORY;
289       }
290
291       /* check if url contains username and/or password */
292       host = strchr(buf, '@');
293       if (host != NULL)
294       {
295          /* Contains username/password, skip it and the @ sign. */
296          host++;
297       }
298       else
299       {
300          /* No username or password. */
301          host = buf;
302       }
303
304       /* check if url contains port */
305       port = strchr(host, ':');
306       if (port != NULL)
307       {
308          /* Contains port */
309          /* Terminate hostname and point to start of port string */
310          *port++ = '\0';
311          http->port = atoi(port);
312       }
313       else
314       {
315          /* No port specified. */
316          http->port = (http->ssl ? 443 : 80);
317       }
318
319       http->host = strdup(host);
320
321       free(buf);
322
323       if (http->host == NULL)
324       {
325          return JB_ERR_MEMORY;
326       }
327    }
328
329    /*
330     * Split domain name so we can compare it against wildcards
331     */
332
333    {
334       char *vec[BUFFER_SIZE];
335       size_t size;
336       char *p;
337
338       http->dbuffer = strdup(http->host);
339       if (NULL == http->dbuffer)
340       {
341          return JB_ERR_MEMORY;
342       }
343
344       /* map to lower case */
345       for (p = http->dbuffer; *p ; p++)
346       {
347          *p = tolower((int)(unsigned char)*p);
348       }
349
350       /* split the domain name into components */
351       http->dcount = ssplit(http->dbuffer, ".", vec, SZ(vec), 1, 1);
352
353       if (http->dcount <= 0)
354       {
355          /*
356           * Error: More than SZ(vec) components in domain
357           *    or: no components in domain
358           */
359          return JB_ERR_PARSE;
360       }
361
362       /* save a copy of the pointers in dvec */
363       size = http->dcount * sizeof(*http->dvec);
364
365       http->dvec = (char **)malloc(size);
366       if (NULL == http->dvec)
367       {
368          return JB_ERR_MEMORY;
369       }
370
371       memcpy(http->dvec, vec, size);
372    }
373
374    return JB_ERR_OK;
375
376 }
377
378
379 /*********************************************************************
380  *
381  * Function    :  parse_http_request
382  *
383  * Description :  Parse out the host and port from the URL.  Find the
384  *                hostname & path, port (if ':'), and/or password (if '@')
385  *
386  * Parameters  :
387  *          1  :  req = HTTP request line to break down
388  *          2  :  http = pointer to the http structure to hold elements
389  *          3  :  csp = Current client state (buffers, headers, etc...)
390  *
391  * Returns     :  JB_ERR_OK on success
392  *                JB_ERR_MEMORY on out of memory
393  *                JB_ERR_CGI_PARAMS on malformed command/URL
394  *                                  or >100 domains deep.
395  *
396  *********************************************************************/
397 jb_err parse_http_request(const char *req,
398                           struct http_request *http,
399                           struct client_state *csp)
400 {
401    char *buf;
402    char *v[10];
403    int n;
404    jb_err err;
405    int is_connect = 0;
406
407    memset(http, '\0', sizeof(*http));
408
409    buf = strdup(req);
410    if (buf == NULL)
411    {
412       return JB_ERR_MEMORY;
413    }
414
415    n = ssplit(buf, " \r\n", v, SZ(v), 1, 1);
416    if (n != 3)
417    {
418       free(buf);
419       return JB_ERR_PARSE;
420    }
421
422    /* this could be a CONNECT request */
423    if (strcmpic(v[0], "connect") == 0)
424    {
425       /* Secure */
426       is_connect = 1;
427    }
428    /* or it could be any other basic HTTP request type */
429    else if ((0 == strcmpic(v[0], "get"))
430          || (0 == strcmpic(v[0], "head"))
431          || (0 == strcmpic(v[0], "post"))
432          || (0 == strcmpic(v[0], "put"))
433          || (0 == strcmpic(v[0], "delete"))
434          || (0 == strcmpic(v[0], "options"))
435          || (0 == strcmpic(v[0], "trace"))
436  
437          /* or a webDAV extension (RFC2518) */
438          || (0 == strcmpic(v[0], "propfind"))
439          || (0 == strcmpic(v[0], "proppatch"))
440          || (0 == strcmpic(v[0], "move"))
441          || (0 == strcmpic(v[0], "copy"))
442          || (0 == strcmpic(v[0], "mkcol"))
443          || (0 == strcmpic(v[0], "lock"))
444          || (0 == strcmpic(v[0], "unlock"))
445
446          /* Or a Microsoft webDAV extension for Exchange 2000.  See: */
447          /*   http://lists.w3.org/Archives/Public/w3c-dist-auth/2002JanMar/0001.html */
448          /*   http://msdn.microsoft.com/library/en-us/wss/wss/_webdav_methods.asp */ 
449          || (0 == strcmpic(v[0], "bcopy"))
450          || (0 == strcmpic(v[0], "bmove"))
451          || (0 == strcmpic(v[0], "bdelete"))
452          || (0 == strcmpic(v[0], "bpropfind"))
453          || (0 == strcmpic(v[0], "bproppatch"))
454
455          /* Or another Microsoft webDAV extension for Exchange 2000.  See: */
456          /*   http://systems.cs.colorado.edu/grunwald/MobileComputing/Papers/draft-cohen-gena-p-base-00.txt */
457          /*   http://lists.w3.org/Archives/Public/w3c-dist-auth/2002JanMar/0001.html */
458          /*   http://msdn.microsoft.com/library/en-us/wss/wss/_webdav_methods.asp */ 
459          || (0 == strcmpic(v[0], "subscribe"))
460          || (0 == strcmpic(v[0], "unsubscribe"))
461          || (0 == strcmpic(v[0], "notify"))
462          || (0 == strcmpic(v[0], "poll"))
463          )
464    {
465       /* Normal */
466       is_connect = 0;
467    }
468    else
469    {
470       /* Unknown HTTP method */
471       free(buf);
472       return JB_ERR_PARSE;
473    }
474
475    err = parse_http_url(v[1], http, csp);
476    if (err)
477    {
478       free(buf);
479       return err;
480    }
481
482    /*
483     * Copy the details into the structure
484     */
485    http->ssl = is_connect;
486    http->cmd = strdup(req);
487    http->gpc = strdup(v[0]);
488    http->ver = strdup(v[2]);
489
490    if ( (http->cmd == NULL)
491      || (http->gpc == NULL)
492      || (http->ver == NULL) )
493    {
494       free(buf);
495       return JB_ERR_MEMORY;
496    }
497
498    free(buf);
499    return JB_ERR_OK;
500
501 }
502
503
504 /*********************************************************************
505  *
506  * Function    :  simple_domaincmp
507  *
508  * Description :  Domain-wise Compare fqdn's.  The comparison is
509  *                both left- and right-anchored.  The individual
510  *                domain names are compared with simplematch().
511  *                This is only used by domain_match.
512  *
513  * Parameters  :
514  *          1  :  pv = array of patterns to compare
515  *          2  :  fv = array of domain components to compare
516  *          3  :  len = length of the arrays (both arrays are the
517  *                      same length - if they weren't, it couldn't
518  *                      possibly be a match).
519  *
520  * Returns     :  0 => domains are equivalent, else no match.
521  *
522  *********************************************************************/
523 static int simple_domaincmp(char **pv, char **fv, int len)
524 {
525    int n;
526
527    for (n = 0; n < len; n++)
528    {
529       if (simplematch(pv[n], fv[n]))
530       {
531          return 1;
532       }
533    }
534
535    return 0;
536
537 }
538
539
540 /*********************************************************************
541  *
542  * Function    :  domain_match
543  *
544  * Description :  Domain-wise Compare fqdn's. Governed by the bimap in
545  *                pattern->unachored, the comparison is un-, left-,
546  *                right-anchored, or both.
547  *                The individual domain names are compared with
548  *                simplematch().
549  *
550  * Parameters  :
551  *          1  :  pattern = a domain that may contain a '*' as a wildcard.
552  *          2  :  fqdn = domain name against which the patterns are compared.
553  *
554  * Returns     :  0 => domains are equivalent, else no match.
555  *
556  *********************************************************************/
557 static int domain_match(const struct url_spec *pattern, const struct http_request *fqdn)
558 {
559    char **pv, **fv;  /* vectors  */
560    int    plen, flen;
561    int unanchored = pattern->unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
562
563    plen = pattern->dcount;
564    flen = fqdn->dcount;
565
566    if (flen < plen)
567    {
568       /* fqdn is too short to match this pattern */
569       return 1;
570    }
571
572    pv   = pattern->dvec;
573    fv   = fqdn->dvec;
574
575    if (unanchored == ANCHOR_LEFT)
576    {
577       /*
578        * Right anchored.
579        *
580        * Convert this into a fully anchored pattern with
581        * the fqdn and pattern the same length
582        */
583       fv += (flen - plen); /* flen - plen >= 0 due to check above */
584       return simple_domaincmp(pv, fv, plen);
585    }
586    else if (unanchored == 0)
587    {
588       /* Fully anchored, check length */
589       if (flen != plen)
590       {
591          return 1;
592       }
593       return simple_domaincmp(pv, fv, plen);
594    }
595    else if (unanchored == ANCHOR_RIGHT)
596    {
597       /* Left anchored, ignore all extra in fqdn */
598       return simple_domaincmp(pv, fv, plen);
599    }
600    else
601    {
602       /* Unanchored */
603       int n;
604       int maxn = flen - plen;
605       for (n = 0; n <= maxn; n++)
606       {
607          if (!simple_domaincmp(pv, fv, plen))
608          {
609             return 0;
610          }
611          /*
612           * Doesn't match from start of fqdn
613           * Try skipping first part of fqdn
614           */
615          fv++;
616       }
617       return 1;
618    }
619
620 }
621
622
623 /*********************************************************************
624  *
625  * Function    :  create_url_spec
626  *
627  * Description :  Creates a "url_spec" structure from a string.
628  *                When finished, free with unload_url().
629  *
630  * Parameters  :
631  *          1  :  url = Target url_spec to be filled in.  Will be
632  *                      zeroed before use.
633  *          2  :  buf = Source pattern, null terminated.  NOTE: The
634  *                      contents of this buffer are destroyed by this
635  *                      function.  If this function succeeds, the
636  *                      buffer is copied to url->spec.  If this
637  *                      function fails, the contents of the buffer
638  *                      are lost forever.
639  *
640  * Returns     :  JB_ERR_OK - Success
641  *                JB_ERR_MEMORY - Out of memory
642  *                JB_ERR_PARSE - Cannot parse regex (Detailed message
643  *                               written to system log)
644  *
645  *********************************************************************/
646 jb_err create_url_spec(struct url_spec * url, const char * buf)
647 {
648    char *p;
649
650    assert(url);
651    assert(buf);
652
653    /* Zero memory */
654    memset(url, '\0', sizeof(*url));
655
656    /* save a copy of the orignal specification */
657    if ((url->spec = strdup(buf)) == NULL)
658    {
659       return JB_ERR_MEMORY;
660    }
661
662    if ((p = strchr(buf, '/')) != NULL)
663    {
664       if (NULL == (url->path = strdup(p)))
665       {
666          freez(url->spec);
667          return JB_ERR_MEMORY;
668       }
669       url->pathlen = strlen(url->path);
670       *p = '\0';
671    }
672    else
673    {
674       url->path    = NULL;
675       url->pathlen = 0;
676    }
677    if (url->path)
678    {
679       int errcode;
680       char rebuf[BUFFER_SIZE];
681
682       if (NULL == (url->preg = zalloc(sizeof(*url->preg))))
683       {
684          freez(url->spec);
685          freez(url->path);
686          return JB_ERR_MEMORY;
687       }
688
689       sprintf(rebuf, "^(%s)", url->path);
690
691       errcode = regcomp(url->preg, rebuf,
692             (REG_EXTENDED|REG_NOSUB|REG_ICASE));
693       if (errcode)
694       {
695          size_t errlen = regerror(errcode,
696             url->preg, rebuf, sizeof(rebuf));
697
698          if (errlen > (sizeof(rebuf) - (size_t)1))
699          {
700             errlen = sizeof(rebuf) - (size_t)1;
701          }
702          rebuf[errlen] = '\0';
703
704          log_error(LOG_LEVEL_ERROR, "error compiling %s: %s",
705             url->spec, rebuf);
706
707          freez(url->spec);
708          freez(url->path);
709          regfree(url->preg);
710          freez(url->preg);
711
712          return JB_ERR_PARSE;
713       }
714    }
715    if ((p = strchr(buf, ':')) == NULL)
716    {
717       url->port = 0;
718    }
719    else
720    {
721       *p++ = '\0';
722       url->port = atoi(p);
723    }
724
725    if (buf[0] != '\0')
726    {
727       char *v[150];
728       size_t size;
729
730       /* Parse domain part */
731       if (buf[strlen(buf) - 1] == '.')
732       {
733          url->unanchored |= ANCHOR_RIGHT;
734       }
735       if (buf[0] == '.')
736       {
737          url->unanchored |= ANCHOR_LEFT;
738       }
739
740       /* split domain into components */
741
742       url->dbuffer = strdup(buf);
743       if (NULL == url->dbuffer)
744       {
745          freez(url->spec);
746          freez(url->path);
747          regfree(url->preg);
748          freez(url->preg);
749          return JB_ERR_MEMORY;
750       }
751
752       /* map to lower case */
753       for (p = url->dbuffer; *p ; p++)
754       {
755          *p = tolower((int)(unsigned char)*p);
756       }
757
758       /* split the domain name into components */
759       url->dcount = ssplit(url->dbuffer, ".", v, SZ(v), 1, 1);
760
761       if (url->dcount < 0)
762       {
763          freez(url->spec);
764          freez(url->path);
765          regfree(url->preg);
766          freez(url->preg);
767          freez(url->dbuffer);
768          url->dcount = 0;
769          return JB_ERR_MEMORY;
770       }
771       else if (url->dcount != 0)
772       {
773
774          /* save a copy of the pointers in dvec */
775          size = url->dcount * sizeof(*url->dvec);
776
777          url->dvec = (char **)malloc(size);
778          if (NULL == url->dvec)
779          {
780             freez(url->spec);
781             freez(url->path);
782             regfree(url->preg);
783             freez(url->preg);
784             freez(url->dbuffer);
785             url->dcount = 0;
786             return JB_ERR_MEMORY;
787          }
788
789          memcpy(url->dvec, v, size);
790       }
791    }
792
793    return JB_ERR_OK;
794
795 }
796
797
798 /*********************************************************************
799  *
800  * Function    :  free_url_spec
801  *
802  * Description :  Called from the "unloaders".  Freez the url
803  *                structure elements.
804  *
805  * Parameters  :
806  *          1  :  url = pointer to a url_spec structure.
807  *
808  * Returns     :  N/A
809  *
810  *********************************************************************/
811 void free_url_spec(struct url_spec *url)
812 {
813    if (url == NULL) return;
814
815    freez(url->spec);
816    freez(url->dbuffer);
817    freez(url->dvec);
818    freez(url->path);
819    if (url->preg)
820    {
821       regfree(url->preg);
822       freez(url->preg);
823    }
824 }
825
826
827 /*********************************************************************
828  *
829  * Function    :  url_match
830  *
831  * Description :  Compare a URL against a URL pattern.
832  *
833  * Parameters  :
834  *          1  :  pattern = a URL pattern
835  *          2  :  url = URL to match
836  *
837  * Returns     :  0 iff the URL matches the pattern, else nonzero.
838  *
839  *********************************************************************/
840 int url_match(const struct url_spec *pattern,
841               const struct http_request *url)
842 {
843    return ((pattern->port == 0) || (pattern->port == url->port))
844        && ((pattern->dbuffer == NULL) || (domain_match(pattern, url) == 0))
845        && ((pattern->path == NULL) ||
846             (regexec(pattern->preg, url->path, 0, NULL, 0) == 0)
847       );
848 }
849
850
851 /*
852   Local Variables:
853   tab-width: 3
854   end:
855 */