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