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