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