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