]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
4de1a914e0562e47e09f8e0308f7f6e853030d99
[openldap] / libraries / libldap / url.c
1 /*
2  *  Copyright (c) 1996 Regents of the University of Michigan.
3  *  All rights reserved.
4  *
5  *  LIBLDAP url.c -- LDAP URL related routines
6  *
7  *  LDAP URLs look like this:
8  *    l d a p : / / hostport / dn [ ? attributes [ ? scope [ ? filter ] ] ]
9  *
10  *  where:
11  *   attributes is a comma separated list
12  *   scope is one of these three strings:  base one sub (default=base)
13  *   filter is an string-represented filter as in RFC 1558
14  *
15  *  e.g.,  ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich
16  *
17  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
18  */
19
20 #include "portable.h"
21
22 #ifndef lint 
23 static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n";
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <ac/ctype.h>
30 #include <ac/socket.h>
31 #include <ac/string.h>
32 #include <ac/time.h>
33
34 #include "ldap-int.h"
35
36
37 /* local functions */
38 static int skip_url_prefix LDAP_P(( char **urlp, int *enclosedp ));
39 static void hex_unescape LDAP_P(( char *s ));
40 static int unhex( char c );
41
42
43 int
44 ldap_is_ldap_url( char *url )
45 {
46         int     enclosed;
47
48         return( url != NULL && skip_url_prefix( &url, &enclosed ));
49 }
50
51
52 static int
53 skip_url_prefix( char **urlp, int *enclosedp )
54 {
55 /*
56  * return non-zero if this looks like a LDAP URL; zero if not
57  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
58  */
59         if ( *urlp == NULL ) {
60                 return( 0 );
61         }
62
63         /* skip leading '<' (if any) */
64         if ( **urlp == '<' ) {
65                 *enclosedp = 1;
66                 ++*urlp;
67         } else {
68                 *enclosedp = 0;
69         }
70
71         /* skip leading "URL:" (if any) */
72         if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp(
73             *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
74                 *urlp += LDAP_URL_URLCOLON_LEN;
75         }
76
77         /* check for missing "ldap://" prefix */
78         if ( strlen( *urlp ) < LDAP_URL_PREFIX_LEN ||
79             strncasecmp( *urlp, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) != 0 ) {
80                 return( 0 );
81         }
82
83         /* skip over "ldap://" prefix and return success */
84         *urlp += LDAP_URL_PREFIX_LEN;
85         return( 1 );
86 }
87
88
89
90 int
91 ldap_url_parse( char *url, LDAPURLDesc **ludpp )
92 {
93 /*
94  *  Pick apart the pieces of an LDAP URL.
95  */
96
97         LDAPURLDesc     *ludp;
98         char            *attrs, *p, *q;
99         int             enclosed, i, nattrs;
100
101         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url, 0, 0 );
102
103         *ludpp = NULL;  /* pessimistic */
104
105         if ( !skip_url_prefix( &url, &enclosed )) {
106                 return( LDAP_URL_ERR_NOTLDAP );
107         }
108
109         /* allocate return struct */
110         if (( ludp = (LDAPURLDesc *)calloc( 1, sizeof( LDAPURLDesc )))
111             == NULLLDAPURLDESC ) {
112                 return( LDAP_URL_ERR_MEM );
113         }
114
115         /* make working copy of the remainder of the URL */
116         if (( url = strdup( url )) == NULL ) {
117                 ldap_free_urldesc( ludp );
118                 return( LDAP_URL_ERR_MEM );
119         }
120
121         if ( enclosed && *((p = url + strlen( url ) - 1)) == '>' ) {
122                 *p = '\0';
123         }
124
125         /* set defaults */
126         ludp->lud_scope = LDAP_SCOPE_BASE;
127         ludp->lud_filter = "(objectClass=*)";
128
129         /* lud_string is the only malloc'd string space we use */
130         ludp->lud_string = url;
131
132         /* scan forward for '/' that marks end of hostport and begin. of dn */
133         if (( ludp->lud_dn = strchr( url, '/' )) == NULL ) {
134                 ldap_free_urldesc( ludp );
135                 return( LDAP_URL_ERR_NODN );
136         }
137
138         /* terminate hostport; point to start of dn */
139         *ludp->lud_dn++ = '\0';
140
141         if (( p = strchr( url, ':' )) != NULL ) {
142                 *p++ = '\0';
143                 ludp->lud_port = atoi( p );
144         }
145
146         if ( *url == '\0' ) {
147                 ludp->lud_host = NULL;
148         } else {
149                 ludp->lud_host = url;
150                 hex_unescape( ludp->lud_host );
151         }
152
153         /* scan for '?' that marks end of dn and beginning of attributes */
154         if (( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) {
155                 /* terminate dn; point to start of attrs. */
156                 *attrs++ = '\0';
157
158                 /* scan for '?' that marks end of attrs and begin. of scope */
159                 if (( p = strchr( attrs, '?' )) != NULL ) {
160                         /*
161                          * terminate attrs; point to start of scope and scan for
162                          * '?' that marks end of scope and begin. of filter
163                          */
164                         *p++ = '\0';
165
166                         if (( q = strchr( p, '?' )) != NULL ) {
167                                 /* terminate scope; point to start of filter */
168                                 *q++ = '\0';
169                                 if ( *q != '\0' ) {
170                                         ludp->lud_filter = q;
171                                         hex_unescape( ludp->lud_filter );
172                                 }
173                         }
174
175                         if ( strcasecmp( p, "one" ) == 0 ) {
176                                 ludp->lud_scope = LDAP_SCOPE_ONELEVEL;
177                         } else if ( strcasecmp( p, "base" ) == 0 ) {
178                                 ludp->lud_scope = LDAP_SCOPE_BASE;
179                         } else if ( strcasecmp( p, "sub" ) == 0 ) {
180                                 ludp->lud_scope = LDAP_SCOPE_SUBTREE;
181                         } else if ( *p != '\0' ) {
182                                 ldap_free_urldesc( ludp );
183                                 return( LDAP_URL_ERR_BADSCOPE );
184                         }
185                 }
186         }
187
188         if ( *ludp->lud_dn == '\0' ) {
189                 ludp->lud_dn = NULL;
190         } else {
191                 hex_unescape( ludp->lud_dn );
192         }
193
194         /*
195          * if attrs list was included, turn it into a null-terminated array
196          */
197         if ( attrs != NULL && *attrs != '\0' ) {
198                 for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) {
199                     if ( *p == ',' ) {
200                             ++nattrs;
201                     }
202                 }
203
204                 if (( ludp->lud_attrs = (char **)calloc( nattrs + 1,
205                     sizeof( char * ))) == NULL ) {
206                         ldap_free_urldesc( ludp );
207                         return( LDAP_URL_ERR_MEM );
208                 }
209
210                 for ( i = 0, p = attrs; i < nattrs; ++i ) {
211                         ludp->lud_attrs[ i ] = p;
212                         if (( p = strchr( p, ',' )) != NULL ) {
213                                 *p++ ='\0';
214                         }
215                         hex_unescape( ludp->lud_attrs[ i ] );
216                 }
217         }
218
219         *ludpp = ludp;
220
221         return( 0 );
222 }
223
224
225 void
226 ldap_free_urldesc( LDAPURLDesc *ludp )
227 {
228         if ( ludp != NULLLDAPURLDESC ) {
229                 if ( ludp->lud_string != NULL ) {
230                         free( ludp->lud_string );
231                 }
232                 if ( ludp->lud_attrs != NULL ) {
233                         free( ludp->lud_attrs );
234                 }
235                 free( ludp );
236         }
237 }
238
239
240
241 int
242 ldap_url_search( LDAP *ld, char *url, int attrsonly )
243 {
244         int             err;
245         LDAPURLDesc     *ludp;
246         BerElement      *ber;
247 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
248         LDAPServer      *srv = NULL;
249 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
250
251         if ( ldap_url_parse( url, &ludp ) != 0 ) {
252                 ld->ld_errno = LDAP_PARAM_ERROR;
253                 return( -1 );
254         }
255
256         if (( ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
257             ludp->lud_filter, ludp->lud_attrs, attrsonly )) == NULLBER ) {
258                 return( -1 );
259         }
260
261         err = 0;
262
263         if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) {
264 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
265                 if (( srv = (LDAPServer *)calloc( 1, sizeof( LDAPServer )))
266                     == NULL || ( srv->lsrv_host = strdup( ludp->lud_host ==
267                     NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) {
268                         if ( srv != NULL ) {
269                                 free( srv );
270                         }
271                         ld->ld_errno = LDAP_NO_MEMORY;
272                         err = -1;
273                 } else {
274                         if ( ludp->lud_port == 0 ) {
275                                 srv->lsrv_port = LDAP_PORT;
276                         } else {
277                                  srv->lsrv_port = ludp->lud_port;
278                         }
279                 }
280 #else /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
281                 ld->ld_errno = LDAP_LOCAL_ERROR;
282                 err = -1;
283 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
284         }
285
286         if ( err != 0 ) {
287                 ber_free( ber, 1 );
288         } else {
289 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
290                 err = ldap_send_server_request( ld, ber, ld->ld_msgid, NULL, srv,
291                     NULL, 1 );
292 #else /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
293                 err = ldap_send_initial_request( ld, LDAP_REQ_SEARCH,
294                     ludp->lud_dn, ber );
295 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
296         }
297
298         ldap_free_urldesc( ludp );
299
300         return( err );
301 }
302
303
304 int
305 ldap_url_search_st( LDAP *ld, char *url, int attrsonly,
306         struct timeval *timeout, LDAPMessage **res )
307 {
308         int     msgid;
309
310         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
311                 return( ld->ld_errno );
312         }
313
314         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
315                 return( ld->ld_errno );
316         }
317
318         if ( ld->ld_errno == LDAP_TIMEOUT ) {
319                 (void) ldap_abandon( ld, msgid );
320                 ld->ld_errno = LDAP_TIMEOUT;
321                 return( ld->ld_errno );
322         }
323
324         return( ldap_result2error( ld, *res, 0 ));
325 }
326
327
328 int
329 ldap_url_search_s( LDAP *ld, char *url, int attrsonly, LDAPMessage **res )
330 {
331         int     msgid;
332
333         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
334                 return( ld->ld_errno );
335         }
336
337         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
338                 return( ld->ld_errno );
339         }
340
341         return( ldap_result2error( ld, *res, 0 ));
342 }
343
344
345 static void
346 hex_unescape( char *s )
347 {
348 /*
349  * Remove URL hex escapes from s... done in place.  The basic concept for
350  * this routine is borrowed from the WWW library HTUnEscape() routine.
351  */
352         char    *p;
353
354         for ( p = s; *s != '\0'; ++s ) {
355                 if ( *s == '%' ) {
356                         if ( *++s != '\0' ) {
357                                 *p = unhex( *s ) << 4;
358                         }
359                         if ( *++s != '\0' ) {
360                                 *p++ += unhex( *s );
361                         }
362                 } else {
363                         *p++ = *s;
364                 }
365         }
366
367         *p = '\0';
368 }
369
370
371 static int
372 unhex( char c )
373 {
374         return( c >= '0' && c <= '9' ? c - '0'
375             : c >= 'A' && c <= 'F' ? c - 'A' + 10
376             : c - 'a' + 10 );
377 }