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