1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251876Speter * contributor license agreements.  See the NOTICE file distributed with
3251876Speter * this work for additional information regarding copyright ownership.
4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251876Speter * (the "License"); you may not use this file except in compliance with
6251876Speter * the License.  You may obtain a copy of the License at
7251876Speter *
8251876Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251876Speter *
10251876Speter * Unless required by applicable law or agreed to in writing, software
11251876Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251876Speter * See the License for the specific language governing permissions and
14251876Speter * limitations under the License.
15251876Speter */
16251876Speter
17251876Speter/* Portions Copyright 1998-2002 The OpenLDAP Foundation
18251876Speter * All rights reserved.
19251876Speter *
20251876Speter * Redistribution and use in source and binary forms, with or without
21251876Speter * modification, are permitted only as authorized by the OpenLDAP
22251876Speter * Public License.  A copy of this license is available at
23251876Speter * http://www.OpenLDAP.org/license.html or in file LICENSE in the
24251876Speter * top-level directory of the distribution.
25251876Speter *
26251876Speter * OpenLDAP is a registered trademark of the OpenLDAP Foundation.
27251876Speter *
28251876Speter * Individual files and/or contributed packages may be copyright by
29251876Speter * other parties and subject to additional restrictions.
30251876Speter *
31251876Speter * This work is derived from the University of Michigan LDAP v3.3
32251876Speter * distribution.  Information concerning this software is available
33251876Speter * at: http://www.umich.edu/~dirsvcs/ldap/
34251876Speter *
35251876Speter * This work also contains materials derived from public sources.
36251876Speter *
37251876Speter * Additional information about OpenLDAP can be obtained at:
38251876Speter *     http://www.openldap.org/
39251876Speter */
40251876Speter
41251876Speter/*
42251876Speter * Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
43251876Speter * All rights reserved.
44251876Speter *
45251876Speter * Redistribution and use in source and binary forms are permitted
46251876Speter * provided that this notice is preserved and that due credit is given
47251876Speter * to the University of Michigan at Ann Arbor. The name of the University
48251876Speter * may not be used to endorse or promote products derived from this
49251876Speter * software without specific prior written permission. This software
50251876Speter * is provided ``as is'' without express or implied warranty.
51251876Speter */
52251876Speter
53251876Speter/*  apr_ldap_url.c -- LDAP URL (RFC 2255) related routines
54251876Speter *
55251876Speter *  Win32 and perhaps other non-OpenLDAP based ldap libraries may be
56251876Speter *  missing ldap_url_* APIs.  We focus here on the one significant
57251876Speter *  aspect, which is parsing.  We have [for the time being] omitted
58251876Speter *  the ldap_url_search APIs.
59251876Speter *
60251876Speter *  LDAP URLs look like this:
61251876Speter *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
62251876Speter *
63251876Speter *  where:
64251876Speter *   attributes is a comma separated list
65251876Speter *   scope is one of these three strings:  base one sub (default=base)
66251876Speter *   filter is an string-represented filter as in RFC 2254
67251876Speter *
68251876Speter *  e.g.,  ldap://host:port/dc=com?o,cn?base?o=openldap?extension
69251876Speter *
70251876Speter *  Tolerates URLs that look like: <ldapurl> and <URL:ldapurl>
71251876Speter */
72251876Speter
73251876Speter#include "apu.h"
74251876Speter#include "apr_pools.h"
75251876Speter#include "apr_general.h"
76251876Speter#include "apr_strings.h"
77251876Speter#include "apr_ldap.h"
78251876Speter
79251876Speter#if APR_HAS_LDAP
80251876Speter
81251876Speter#if APR_HAVE_STDLIB_H
82251876Speter#include <stdlib.h>
83251876Speter#endif
84251876Speter
85251876Speter#ifndef LDAPS_PORT
86251876Speter#define LDAPS_PORT              636  /* ldaps:/// default LDAP over TLS port */
87251876Speter#endif
88251876Speter
89251876Speter#define APR_LDAP_URL_PREFIX         "ldap://"
90251876Speter#define APR_LDAP_URL_PREFIX_LEN     (sizeof(APR_LDAP_URL_PREFIX)-1)
91251876Speter#define APR_LDAPS_URL_PREFIX        "ldaps://"
92251876Speter#define APR_LDAPS_URL_PREFIX_LEN    (sizeof(APR_LDAPS_URL_PREFIX)-1)
93251876Speter#define APR_LDAPI_URL_PREFIX        "ldapi://"
94251876Speter#define APR_LDAPI_URL_PREFIX_LEN    (sizeof(APR_LDAPI_URL_PREFIX)-1)
95251876Speter#define APR_LDAP_URL_URLCOLON       "URL:"
96251876Speter#define APR_LDAP_URL_URLCOLON_LEN   (sizeof(APR_LDAP_URL_URLCOLON)-1)
97251876Speter
98251876Speter
99251876Speter/* local functions */
100251876Speterstatic const char* skip_url_prefix(const char *url,
101251876Speter                                   int *enclosedp,
102251876Speter                                   const char **scheme);
103251876Speter
104251876Speterstatic void apr_ldap_pvt_hex_unescape(char *s);
105251876Speter
106251876Speterstatic int apr_ldap_pvt_unhex(int c);
107251876Speter
108251876Speterstatic char **apr_ldap_str2charray(apr_pool_t *pool,
109251876Speter                                   const char *str,
110251876Speter                                   const char *brkstr);
111251876Speter
112251876Speter
113251876Speter/**
114251876Speter * Is this URL an ldap url?
115251876Speter *
116251876Speter */
117251876SpeterAPU_DECLARE(int) apr_ldap_is_ldap_url(const char *url)
118251876Speter{
119251876Speter    int enclosed;
120251876Speter    const char * scheme;
121251876Speter
122251876Speter    if( url == NULL ) {
123251876Speter        return 0;
124251876Speter    }
125251876Speter
126251876Speter    if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
127251876Speter        return 0;
128251876Speter    }
129251876Speter
130251876Speter    return 1;
131251876Speter}
132251876Speter
133251876Speter/**
134251876Speter * Is this URL a secure ldap url?
135251876Speter *
136251876Speter */
137251876SpeterAPU_DECLARE(int) apr_ldap_is_ldaps_url(const char *url)
138251876Speter{
139251876Speter    int enclosed;
140251876Speter    const char * scheme;
141251876Speter
142251876Speter    if( url == NULL ) {
143251876Speter        return 0;
144251876Speter    }
145251876Speter
146251876Speter    if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
147251876Speter        return 0;
148251876Speter    }
149251876Speter
150251876Speter    return strcmp(scheme, "ldaps") == 0;
151251876Speter}
152251876Speter
153251876Speter/**
154251876Speter * Is this URL an ldap socket url?
155251876Speter *
156251876Speter */
157251876SpeterAPU_DECLARE(int) apr_ldap_is_ldapi_url(const char *url)
158251876Speter{
159251876Speter    int enclosed;
160251876Speter    const char * scheme;
161251876Speter
162251876Speter    if( url == NULL ) {
163251876Speter        return 0;
164251876Speter    }
165251876Speter
166251876Speter    if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
167251876Speter        return 0;
168251876Speter    }
169251876Speter
170251876Speter    return strcmp(scheme, "ldapi") == 0;
171251876Speter}
172251876Speter
173251876Speter
174251876Speterstatic const char *skip_url_prefix(const char *url, int *enclosedp,
175251876Speter                                   const char **scheme)
176251876Speter{
177251876Speter    /*
178251876Speter     * return non-zero if this looks like a LDAP URL; zero if not
179251876Speter     * if non-zero returned, *urlp will be moved past "ldap://" part of URL
180251876Speter     */
181251876Speter    const char *p;
182251876Speter
183251876Speter    if ( url == NULL ) {
184251876Speter        return( NULL );
185251876Speter    }
186251876Speter
187251876Speter    p = url;
188251876Speter
189251876Speter    /* skip leading '<' (if any) */
190251876Speter    if ( *p == '<' ) {
191251876Speter        *enclosedp = 1;
192251876Speter        ++p;
193251876Speter    } else {
194251876Speter        *enclosedp = 0;
195251876Speter    }
196251876Speter
197251876Speter    /* skip leading "URL:" (if any) */
198251876Speter    if ( strncasecmp( p, APR_LDAP_URL_URLCOLON, APR_LDAP_URL_URLCOLON_LEN ) == 0 ) {
199251876Speter        p += APR_LDAP_URL_URLCOLON_LEN;
200251876Speter    }
201251876Speter
202251876Speter    /* check for "ldap://" prefix */
203251876Speter    if ( strncasecmp( p, APR_LDAP_URL_PREFIX, APR_LDAP_URL_PREFIX_LEN ) == 0 ) {
204251876Speter        /* skip over "ldap://" prefix and return success */
205251876Speter        p += APR_LDAP_URL_PREFIX_LEN;
206251876Speter        *scheme = "ldap";
207251876Speter        return( p );
208251876Speter    }
209251876Speter
210251876Speter    /* check for "ldaps://" prefix */
211251876Speter    if ( strncasecmp( p, APR_LDAPS_URL_PREFIX, APR_LDAPS_URL_PREFIX_LEN ) == 0 ) {
212251876Speter        /* skip over "ldaps://" prefix and return success */
213251876Speter        p += APR_LDAPS_URL_PREFIX_LEN;
214251876Speter        *scheme = "ldaps";
215251876Speter        return( p );
216251876Speter    }
217251876Speter
218251876Speter    /* check for "ldapi://" prefix */
219251876Speter    if ( strncasecmp( p, APR_LDAPI_URL_PREFIX, APR_LDAPI_URL_PREFIX_LEN ) == 0 ) {
220251876Speter        /* skip over "ldapi://" prefix and return success */
221251876Speter        p += APR_LDAPI_URL_PREFIX_LEN;
222251876Speter        *scheme = "ldapi";
223251876Speter        return( p );
224251876Speter    }
225251876Speter
226251876Speter    return( NULL );
227251876Speter}
228251876Speter
229251876Speter
230251876Speterstatic int str2scope(const char *p)
231251876Speter{
232251876Speter    if ( strcasecmp( p, "one" ) == 0 ) {
233251876Speter        return LDAP_SCOPE_ONELEVEL;
234251876Speter
235251876Speter    } else if ( strcasecmp( p, "onetree" ) == 0 ) {
236251876Speter        return LDAP_SCOPE_ONELEVEL;
237251876Speter
238251876Speter    } else if ( strcasecmp( p, "base" ) == 0 ) {
239251876Speter        return LDAP_SCOPE_BASE;
240251876Speter
241251876Speter    } else if ( strcasecmp( p, "sub" ) == 0 ) {
242251876Speter        return LDAP_SCOPE_SUBTREE;
243251876Speter
244251876Speter    } else if ( strcasecmp( p, "subtree" ) == 0 ) {
245251876Speter        return LDAP_SCOPE_SUBTREE;
246251876Speter    }
247251876Speter
248251876Speter    return( -1 );
249251876Speter}
250251876Speter
251251876Speter
252251876Speter/**
253251876Speter * Parse the URL provided into an apr_ldap_url_desc_t object.
254251876Speter *
255251876Speter * APR_SUCCESS is returned on success, APR_EGENERAL on failure.
256251876Speter * The LDAP result code and reason string is returned in the
257251876Speter * apr_ldap_err_t structure.
258251876Speter */
259251876SpeterAPU_DECLARE(int) apr_ldap_url_parse_ext(apr_pool_t *pool,
260251876Speter                                        const char *url_in,
261251876Speter                                        apr_ldap_url_desc_t **ludpp,
262251876Speter                                        apr_ldap_err_t **result_err)
263251876Speter{
264251876Speter    apr_ldap_url_desc_t *ludp;
265251876Speter    char        *p, *q, *r;
266251876Speter    int         i, enclosed;
267251876Speter    const char  *scheme = NULL;
268251876Speter    const char  *url_tmp;
269251876Speter    char        *url;
270251876Speter
271251876Speter    apr_ldap_err_t *result = (apr_ldap_err_t *)apr_pcalloc(pool, sizeof(apr_ldap_err_t));
272251876Speter    *result_err = result;
273251876Speter
274251876Speter    /* sanity check our parameters */
275251876Speter    if( url_in == NULL || ludpp == NULL ) {
276251876Speter        result->reason = "Either the LDAP URL, or the URL structure was NULL. Oops.";
277251876Speter        result->rc = APR_LDAP_URL_ERR_PARAM;
278251876Speter        return APR_EGENERAL;
279251876Speter    }
280251876Speter
281251876Speter    *ludpp = NULL;  /* pessimistic */
282251876Speter
283251876Speter    url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
284251876Speter    if ( url_tmp == NULL ) {
285251876Speter        result->reason = "The scheme was not recognised as a valid LDAP URL scheme.";
286251876Speter        result->rc = APR_LDAP_URL_ERR_BADSCHEME;
287251876Speter        return APR_EGENERAL;
288251876Speter    }
289251876Speter
290251876Speter    /* make working copy of the remainder of the URL */
291251876Speter    url = (char *)apr_pstrdup(pool, url_tmp);
292251876Speter    if ( url == NULL ) {
293251876Speter        result->reason = "Out of memory parsing LDAP URL.";
294251876Speter        result->rc = APR_LDAP_URL_ERR_MEM;
295251876Speter        return APR_EGENERAL;
296251876Speter    }
297251876Speter
298251876Speter    if ( enclosed ) {
299251876Speter        p = &url[strlen(url)-1];
300251876Speter
301251876Speter        if( *p != '>' ) {
302251876Speter            result->reason = "Bad enclosure error while parsing LDAP URL.";
303251876Speter            result->rc = APR_LDAP_URL_ERR_BADENCLOSURE;
304251876Speter            return APR_EGENERAL;
305251876Speter        }
306251876Speter
307251876Speter        *p = '\0';
308251876Speter    }
309251876Speter
310251876Speter    /* allocate return struct */
311251876Speter    ludp = (apr_ldap_url_desc_t *)apr_pcalloc(pool, sizeof(apr_ldap_url_desc_t));
312251876Speter    if ( ludp == NULL ) {
313251876Speter        result->reason = "Out of memory parsing LDAP URL.";
314251876Speter        result->rc = APR_LDAP_URL_ERR_MEM;
315251876Speter        return APR_EGENERAL;
316251876Speter    }
317251876Speter
318251876Speter    ludp->lud_next = NULL;
319251876Speter    ludp->lud_host = NULL;
320251876Speter    ludp->lud_port = LDAP_PORT;
321251876Speter    ludp->lud_dn = NULL;
322251876Speter    ludp->lud_attrs = NULL;
323251876Speter    ludp->lud_filter = NULL;
324251876Speter    ludp->lud_scope = -1;
325251876Speter    ludp->lud_filter = NULL;
326251876Speter    ludp->lud_exts = NULL;
327251876Speter
328251876Speter    ludp->lud_scheme = (char *)apr_pstrdup(pool, scheme);
329251876Speter    if ( ludp->lud_scheme == NULL ) {
330251876Speter        result->reason = "Out of memory parsing LDAP URL.";
331251876Speter        result->rc = APR_LDAP_URL_ERR_MEM;
332251876Speter        return APR_EGENERAL;
333251876Speter    }
334251876Speter
335251876Speter    if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
336251876Speter        ludp->lud_port = LDAPS_PORT;
337251876Speter    }
338251876Speter
339251876Speter    /* scan forward for '/' that marks end of hostport and begin. of dn */
340251876Speter    p = strchr( url, '/' );
341251876Speter
342251876Speter    if( p != NULL ) {
343251876Speter        /* terminate hostport; point to start of dn */
344251876Speter        *p++ = '\0';
345251876Speter    }
346251876Speter
347251876Speter    /* IPv6 syntax with [ip address]:port */
348251876Speter    if ( *url == '[' ) {
349251876Speter        r = strchr( url, ']' );
350251876Speter        if ( r == NULL ) {
351251876Speter            result->reason = "Bad LDAP URL while parsing IPV6 syntax.";
352251876Speter            result->rc = APR_LDAP_URL_ERR_BADURL;
353251876Speter            return APR_EGENERAL;
354251876Speter        }
355251876Speter        *r++ = '\0';
356251876Speter        q = strrchr( r, ':' );
357251876Speter    } else {
358251876Speter        q = strrchr( url, ':' );
359251876Speter    }
360251876Speter
361251876Speter    if ( q != NULL ) {
362251876Speter        apr_ldap_pvt_hex_unescape( ++q );
363251876Speter
364251876Speter        if( *q == '\0' ) {
365251876Speter            result->reason = "Bad LDAP URL while parsing.";
366251876Speter            result->rc = APR_LDAP_URL_ERR_BADURL;
367251876Speter            return APR_EGENERAL;
368251876Speter        }
369251876Speter
370251876Speter        ludp->lud_port = atoi( q );
371251876Speter    }
372251876Speter
373251876Speter    apr_ldap_pvt_hex_unescape( url );
374251876Speter
375251876Speter    /* If [ip address]:port syntax, url is [ip and we skip the [ */
376251876Speter    ludp->lud_host = (char *)apr_pstrdup(pool, url + ( *url == '[' ));
377251876Speter    if( ludp->lud_host == NULL ) {
378251876Speter        result->reason = "Out of memory parsing LDAP URL.";
379251876Speter        result->rc = APR_LDAP_URL_ERR_MEM;
380251876Speter        return APR_EGENERAL;
381251876Speter    }
382251876Speter
383251876Speter    /*
384251876Speter     * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
385251876Speter     *
386251876Speter     * On early Novell releases, search references/referrals were returned
387251876Speter     * in this format, i.e., the dn was kind of in the scope position,
388251876Speter     * but the required slash is missing. The whole thing is illegal syntax,
389251876Speter     * but we need to account for it. Fortunately it can't be confused with
390251876Speter     * anything real.
391251876Speter     */
392251876Speter    if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
393251876Speter        q++;
394251876Speter        /* ? immediately followed by question */
395251876Speter        if( *q == '?') {
396251876Speter            q++;
397251876Speter            if( *q != '\0' ) {
398251876Speter                /* parse dn part */
399251876Speter                apr_ldap_pvt_hex_unescape( q );
400251876Speter                ludp->lud_dn = (char *)apr_pstrdup(pool, q);
401251876Speter            } else {
402251876Speter                ludp->lud_dn = (char *)apr_pstrdup(pool, "");
403251876Speter            }
404251876Speter
405251876Speter            if( ludp->lud_dn == NULL ) {
406251876Speter                result->reason = "Out of memory parsing LDAP URL.";
407251876Speter                result->rc = APR_LDAP_URL_ERR_MEM;
408251876Speter                return APR_EGENERAL;
409251876Speter            }
410251876Speter        }
411251876Speter    }
412251876Speter
413251876Speter    if( p == NULL ) {
414251876Speter        *ludpp = ludp;
415251876Speter        return APR_SUCCESS;
416251876Speter    }
417251876Speter
418251876Speter    /* scan forward for '?' that may marks end of dn */
419251876Speter    q = strchr( p, '?' );
420251876Speter
421251876Speter    if( q != NULL ) {
422251876Speter        /* terminate dn part */
423251876Speter        *q++ = '\0';
424251876Speter    }
425251876Speter
426251876Speter    if( *p != '\0' ) {
427251876Speter        /* parse dn part */
428251876Speter        apr_ldap_pvt_hex_unescape( p );
429251876Speter        ludp->lud_dn = (char *)apr_pstrdup(pool, p);
430251876Speter    } else {
431251876Speter        ludp->lud_dn = (char *)apr_pstrdup(pool, "");
432251876Speter    }
433251876Speter
434251876Speter    if( ludp->lud_dn == NULL ) {
435251876Speter        result->reason = "Out of memory parsing LDAP URL.";
436251876Speter        result->rc = APR_LDAP_URL_ERR_MEM;
437251876Speter        return APR_EGENERAL;
438251876Speter    }
439251876Speter
440251876Speter    if( q == NULL ) {
441251876Speter        /* no more */
442251876Speter        *ludpp = ludp;
443251876Speter        return APR_SUCCESS;
444251876Speter    }
445251876Speter
446251876Speter    /* scan forward for '?' that may marks end of attributes */
447251876Speter    p = q;
448251876Speter    q = strchr( p, '?' );
449251876Speter
450251876Speter    if( q != NULL ) {
451251876Speter        /* terminate attributes part */
452251876Speter        *q++ = '\0';
453251876Speter    }
454251876Speter
455251876Speter    if( *p != '\0' ) {
456251876Speter        /* parse attributes */
457251876Speter        apr_ldap_pvt_hex_unescape( p );
458251876Speter        ludp->lud_attrs = apr_ldap_str2charray(pool, p, ",");
459251876Speter
460251876Speter        if( ludp->lud_attrs == NULL ) {
461251876Speter            result->reason = "Bad attributes encountered while parsing LDAP URL.";
462251876Speter            result->rc = APR_LDAP_URL_ERR_BADATTRS;
463251876Speter            return APR_EGENERAL;
464251876Speter        }
465251876Speter    }
466251876Speter
467251876Speter    if ( q == NULL ) {
468251876Speter        /* no more */
469251876Speter        *ludpp = ludp;
470251876Speter        return APR_SUCCESS;
471251876Speter    }
472251876Speter
473251876Speter    /* scan forward for '?' that may marks end of scope */
474251876Speter    p = q;
475251876Speter    q = strchr( p, '?' );
476251876Speter
477251876Speter    if( q != NULL ) {
478251876Speter        /* terminate the scope part */
479251876Speter        *q++ = '\0';
480251876Speter    }
481251876Speter
482251876Speter    if( *p != '\0' ) {
483251876Speter        /* parse the scope */
484251876Speter        apr_ldap_pvt_hex_unescape( p );
485251876Speter        ludp->lud_scope = str2scope( p );
486251876Speter
487251876Speter        if( ludp->lud_scope == -1 ) {
488251876Speter            result->reason = "Bad scope encountered while parsing LDAP URL.";
489251876Speter            result->rc = APR_LDAP_URL_ERR_BADSCOPE;
490251876Speter            return APR_EGENERAL;
491251876Speter        }
492251876Speter    }
493251876Speter
494251876Speter    if ( q == NULL ) {
495251876Speter        /* no more */
496251876Speter        *ludpp = ludp;
497251876Speter        return APR_SUCCESS;
498251876Speter    }
499251876Speter
500251876Speter    /* scan forward for '?' that may marks end of filter */
501251876Speter    p = q;
502251876Speter    q = strchr( p, '?' );
503251876Speter
504251876Speter    if( q != NULL ) {
505251876Speter        /* terminate the filter part */
506251876Speter        *q++ = '\0';
507251876Speter    }
508251876Speter
509251876Speter    if( *p != '\0' ) {
510251876Speter        /* parse the filter */
511251876Speter        apr_ldap_pvt_hex_unescape( p );
512251876Speter
513251876Speter        if( ! *p ) {
514251876Speter            /* missing filter */
515251876Speter            result->reason = "Bad filter encountered while parsing LDAP URL.";
516251876Speter            result->rc = APR_LDAP_URL_ERR_BADFILTER;
517251876Speter            return APR_EGENERAL;
518251876Speter        }
519251876Speter
520251876Speter        ludp->lud_filter = (char *)apr_pstrdup(pool, p);
521251876Speter        if( ludp->lud_filter == NULL ) {
522251876Speter            result->reason = "Out of memory parsing LDAP URL.";
523251876Speter            result->rc = APR_LDAP_URL_ERR_MEM;
524251876Speter            return APR_EGENERAL;
525251876Speter        }
526251876Speter    }
527251876Speter
528251876Speter    if ( q == NULL ) {
529251876Speter        /* no more */
530251876Speter        *ludpp = ludp;
531251876Speter        return APR_SUCCESS;
532251876Speter    }
533251876Speter
534251876Speter    /* scan forward for '?' that may marks end of extensions */
535251876Speter    p = q;
536251876Speter    q = strchr( p, '?' );
537251876Speter
538251876Speter    if( q != NULL ) {
539251876Speter        /* extra '?' */
540251876Speter        result->reason = "Bad URL encountered while parsing LDAP URL.";
541251876Speter        result->rc = APR_LDAP_URL_ERR_BADURL;
542251876Speter        return APR_EGENERAL;
543251876Speter    }
544251876Speter
545251876Speter    /* parse the extensions */
546251876Speter    ludp->lud_exts = apr_ldap_str2charray(pool, p, ",");
547251876Speter    if( ludp->lud_exts == NULL ) {
548251876Speter        result->reason = "Bad extensions encountered while parsing LDAP URL.";
549251876Speter        result->rc = APR_LDAP_URL_ERR_BADEXTS;
550251876Speter        return APR_EGENERAL;
551251876Speter    }
552251876Speter
553251876Speter    for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
554251876Speter        apr_ldap_pvt_hex_unescape( ludp->lud_exts[i] );
555251876Speter
556251876Speter        if( *ludp->lud_exts[i] == '!' ) {
557251876Speter            /* count the number of critical extensions */
558251876Speter            ludp->lud_crit_exts++;
559251876Speter        }
560251876Speter    }
561251876Speter
562251876Speter    if( i == 0 ) {
563251876Speter        /* must have 1 or more */
564251876Speter        result->reason = "Bad extensions encountered while parsing LDAP URL.";
565251876Speter        result->rc = APR_LDAP_URL_ERR_BADEXTS;
566251876Speter        return APR_EGENERAL;
567251876Speter    }
568251876Speter
569251876Speter    /* no more */
570251876Speter    *ludpp = ludp;
571251876Speter    return APR_SUCCESS;
572251876Speter}
573251876Speter
574251876Speter
575251876Speter/**
576251876Speter * Parse the URL provided into an apr_ldap_url_desc_t object.
577251876Speter *
578251876Speter * APR_SUCCESS is returned on success, APR_EGENERAL on failure.
579251876Speter * The LDAP result code and reason string is returned in the
580251876Speter * apr_ldap_err_t structure.
581251876Speter */
582251876SpeterAPU_DECLARE(int) apr_ldap_url_parse(apr_pool_t *pool,
583251876Speter                                    const char *url_in,
584251876Speter                                    apr_ldap_url_desc_t **ludpp,
585251876Speter                                    apr_ldap_err_t **result_err)
586251876Speter{
587251876Speter
588251876Speter    int rc = apr_ldap_url_parse_ext(pool, url_in, ludpp, result_err);
589251876Speter    if( rc != APR_SUCCESS ) {
590251876Speter        return rc;
591251876Speter    }
592251876Speter
593251876Speter    if ((*ludpp)->lud_scope == -1) {
594251876Speter        (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
595251876Speter    }
596251876Speter
597251876Speter    if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
598251876Speter        (*ludpp)->lud_host = NULL;
599251876Speter    }
600251876Speter
601251876Speter    return rc;
602251876Speter
603251876Speter}
604251876Speter
605251876Speter
606251876Speterstatic void apr_ldap_pvt_hex_unescape(char *s)
607251876Speter{
608251876Speter    /*
609251876Speter     * Remove URL hex escapes from s... done in place.  The basic concept for
610251876Speter     * this routine is borrowed from the WWW library HTUnEscape() routine.
611251876Speter     */
612251876Speter    char    *p;
613251876Speter
614251876Speter    for ( p = s; *s != '\0'; ++s ) {
615251876Speter        if ( *s == '%' ) {
616251876Speter            if ( *++s == '\0' ) {
617251876Speter                break;
618251876Speter            }
619251876Speter            *p = apr_ldap_pvt_unhex( *s ) << 4;
620251876Speter            if ( *++s == '\0' ) {
621251876Speter                break;
622251876Speter            }
623251876Speter            *p++ += apr_ldap_pvt_unhex( *s );
624251876Speter        } else {
625251876Speter            *p++ = *s;
626251876Speter        }
627251876Speter    }
628251876Speter
629251876Speter    *p = '\0';
630251876Speter}
631251876Speter
632251876Speter
633251876Speterstatic int apr_ldap_pvt_unhex(int c)
634251876Speter{
635251876Speter    return( c >= '0' && c <= '9' ? c - '0'
636251876Speter        : c >= 'A' && c <= 'F' ? c - 'A' + 10
637251876Speter        : c - 'a' + 10 );
638251876Speter}
639251876Speter
640251876Speter
641251876Speter/**
642251876Speter * Convert a string to a character array
643251876Speter */
644251876Speterstatic char **apr_ldap_str2charray(apr_pool_t *pool,
645251876Speter                                   const char *str_in,
646251876Speter                                   const char *brkstr)
647251876Speter{
648251876Speter    char    **res;
649251876Speter    char    *str, *s;
650251876Speter    char    *lasts;
651251876Speter    int i;
652251876Speter
653251876Speter    /* protect the input string from strtok */
654251876Speter    str = (char *)apr_pstrdup(pool, str_in);
655251876Speter    if( str == NULL ) {
656251876Speter        return NULL;
657251876Speter    }
658251876Speter
659251876Speter    i = 1;
660251876Speter    for ( s = str; *s; s++ ) {
661251876Speter        /* Warning: this strchr was previously ldap_utf8_strchr(), check
662251876Speter         * whether this particular code has any charset issues.
663251876Speter         */
664251876Speter        if ( strchr( brkstr, *s ) != NULL ) {
665251876Speter            i++;
666251876Speter        }
667251876Speter    }
668251876Speter
669251876Speter    res = (char **) apr_pcalloc(pool, (i + 1) * sizeof(char *));
670251876Speter    if( res == NULL ) {
671251876Speter        return NULL;
672251876Speter    }
673251876Speter
674251876Speter    i = 0;
675251876Speter
676251876Speter    for ( s = (char *)apr_strtok( str, brkstr, &lasts );
677251876Speter          s != NULL;
678251876Speter          s = (char *)apr_strtok( NULL, brkstr, &lasts ) ) {
679251876Speter
680251876Speter        res[i] = (char *)apr_pstrdup(pool, s);
681251876Speter        if(res[i] == NULL) {
682251876Speter            return NULL;
683251876Speter        }
684251876Speter
685251876Speter        i++;
686251876Speter    }
687251876Speter
688251876Speter    res[i] = NULL;
689251876Speter
690251876Speter    return( res );
691251876Speter
692251876Speter}
693251876Speter
694251876Speter#endif /* APR_HAS_LDAP */
695