1 /*
2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8 /*
9 * Copyright (c) 1996 Regents of the University of Michigan.
10 * All rights reserved.
11 *
12 * LIBLDAP url.c -- LDAP URL related routines
13 *
14 * LDAP URLs look like this:
15 * l d a p : / / hostport / dn [ ? attributes [ ? scope [ ? filter [ ? extensions ] ] ] ]
16 *
17 * where:
18 * attributes is a comma separated list
19 * scope is one of these three strings: base one sub (default=base)
20 * filter is an string-represented filter as in RFC 1558
21 * extensions is a comma separated list of extension
22 * and extension is like this: [ ! ] oid/x-oid [ = value ]
23 *
24 * e.g., ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich
25 *
26 * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
27 */
28
29 #ifndef lint
30 static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n";
31 #endif
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #ifdef MACOS
38 #include <stdlib.h>
39 #include "macos.h"
40 #endif /* MACOS */
41
42 #if defined( DOS ) || defined( _WIN32 )
43 #include <stdlib.h>
44 #include <malloc.h>
45 #include "msdos.h"
46 #endif /* DOS || _WIN32 */
47
48 #if !defined(MACOS) && !defined(DOS) && !defined( _WIN32 )
49 #include <sys/time.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #endif /* !MACOS && !DOS && !_WIN32 */
53
54 #include "lber.h"
55 #include "ldap.h"
56 #include "ldap-private.h"
57 #include "ldap-int.h"
58
59
60 #ifdef NEEDPROTOS
61 static int skip_url_prefix( char **urlp, int *enclosedp );
62 static void hex_unescape( char *s );
63 static int unhex( char c );
64 #else /* NEEDPROTOS */
65 static int skip_url_prefix();
66 static void hex_unescape();
67 static int unhex();
68 #endif /* NEEDPROTOS */
69
70
71 int
ldap_is_ldap_url(char * url)72 ldap_is_ldap_url( char *url )
73 {
74 int enclosed;
75
76 return( url != NULL && skip_url_prefix( &url, &enclosed ));
77 }
78
79
80 static int
skip_url_prefix(char ** urlp,int * enclosedp)81 skip_url_prefix( char **urlp, int *enclosedp )
82 {
83 /*
84 * return non-zero if this looks like a LDAP URL; zero if not
85 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
86 */
87 if ( *urlp == NULL ) {
88 return( 0 );
89 }
90
91 /* skip leading '<' (if any) */
92 if ( **urlp == '<' ) {
93 *enclosedp = 1;
94 ++*urlp;
95 } else {
96 *enclosedp = 0;
97 }
98
99 /* skip leading "URL:" (if any) */
100 if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp(
101 *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
102 *urlp += LDAP_URL_URLCOLON_LEN;
103 }
104
105 /* check for missing "ldap://" prefix */
106 if ( strlen( *urlp ) < LDAP_URL_PREFIX_LEN ||
107 strncasecmp( *urlp, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) != 0 ) {
108 return( 0 );
109 }
110
111 /* skip over "ldap://" prefix and return success */
112 *urlp += LDAP_URL_PREFIX_LEN;
113 return( 1 );
114 }
115
ldap_url_extension_parse(char * exts,LDAPURLExt *** lueppp)116 int ldap_url_extension_parse( char *exts, LDAPURLExt *** lueppp)
117 {
118 /* Pick apart the pieces of an LDAP URL Extensions */
119 /* No copy of exts is made, LDAPURLExt's points to exts string */
120 LDAPURLExt ** lues;
121 LDAPURLExt *luep;
122 int i = 0;
123 char *p = exts;
124 char *ptr, *ptr2;
125
126 *lueppp = NULL;
127
128 /* Count the number of , in extensions */
129 while ( (p = strchr (p, ',')) != NULL){
130 i++;
131 }
132 /* There are at most i+1 extensions */
133 if ((lues = (LDAPURLExt **)calloc(i+2, sizeof(LDAPURLExt *))) == NULL){
134 return (LDAP_URL_ERR_MEM);
135 }
136
137 p = exts;
138 i = 0;
139
140 while ( p ) {
141 if ((ptr = strchr(p, ',')) != NULL)
142 *ptr++ = '\0';
143 else
144 ptr = NULL;
145
146 if ((luep = (LDAPURLExt *)calloc(1, sizeof(LDAPURLExt))) == NULL){
147 ldap_free_urlexts(lues);
148 return (LDAP_URL_ERR_MEM);
149 }
150 lues[i] = luep;
151
152 if (*p == '!'){
153 luep->lue_iscritical = 1;
154 p++;
155 }
156 luep->lue_type = p;
157
158 if (( ptr2 = strchr(p, '=')) != NULL) {
159 *ptr2++ = '\0';
160 luep->lue_value = ptr2;
161 hex_unescape(ptr2);
162 }
163
164 i++;
165 p = ptr;
166 }
167 *lueppp = lues;
168
169 return( 0 );
170 }
171
172
173 int
ldap_url_parse(char * url,LDAPURLDesc ** ludpp)174 ldap_url_parse( char *url, LDAPURLDesc **ludpp )
175 {
176 /*
177 * Pick apart the pieces of an LDAP URL.
178 */
179
180 LDAPURLDesc *ludp;
181 char *attrs = NULL;
182 char *p = NULL;
183 char *q = NULL;
184 char *x = NULL;
185 int enclosed, i, nattrs, errcode;
186
187 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 262, "ldap_url_parse(%s)\n"), url, 0, 0 );
188
189 *ludpp = NULL; /* pessimistic */
190
191 if ( !skip_url_prefix( &url, &enclosed )) {
192 return( LDAP_URL_ERR_NOTLDAP );
193 }
194
195 /* allocate return struct */
196 if (( ludp = (LDAPURLDesc *)calloc( 1, sizeof( LDAPURLDesc )))
197 == NULLLDAPURLDESC ) {
198 return( LDAP_URL_ERR_MEM );
199 }
200
201 ludp->lud_port = LDAP_PORT;
202
203 /* make working copy of the remainder of the URL */
204 if (( url = strdup( url )) == NULL ) {
205 ldap_free_urldesc( ludp );
206 return( LDAP_URL_ERR_MEM );
207 }
208
209 if ( enclosed && *((p = url + strlen( url ) - 1)) == '>' ) {
210 *p = '\0';
211 }
212
213 /* set defaults */
214 /* LP By default don't set them... Then we can check if they are present or not in URL */
215 ludp->lud_scope = LDAP_SCOPE_UNKNOWN;
216 ludp->lud_filter = NULL;
217
218
219 /* lud_string is the only malloc'd string space we use */
220 ludp->lud_string = url;
221
222 /* scan forward for '/' that marks end of hostport and begin. of dn */
223 if (( ludp->lud_dn = strchr( url, '/' )) != NULL ) {
224 *ludp->lud_dn++ = '\0';
225 }
226
227 /* terminate hostport; point to start of dn */
228
229 if (( p = strchr( url, ':' )) != NULL ) {
230 *p++ = '\0';
231 ludp->lud_port = atoi( p );
232 }
233
234 if ( *url == '\0' ) {
235 ludp->lud_host = NULL;
236 } else {
237 ludp->lud_host = url;
238 hex_unescape( ludp->lud_host );
239 }
240
241 if (ludp->lud_dn != NULL){
242 /* scan for '?' that marks end of dn and beginning of attributes */
243 if (( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) {
244 /* terminate dn; point to start of attrs. */
245 *attrs++ = '\0';
246
247 /* scan for '?' that marks end of attrs and begin. of scope */
248 if (( p = strchr( attrs, '?' )) != NULL ) {
249 /*
250 * terminate attrs; point to start of scope and scan for
251 * '?' that marks end of scope and begin. of filter
252 */
253 *p++ = '\0';
254
255 if (( q = strchr( p, '?' )) != NULL ) {
256 /* terminate scope; point to start of filter */
257 *q++ = '\0';
258
259 if (( x = strchr(q, '?')) != NULL ) {
260 /* terminate filter; point to start of extension */
261 *x++ = '\0';
262
263 if (*x != '\0'){
264 /* parse extensions */
265 }
266 }
267
268 if ( *q != '\0' ) {
269 ludp->lud_filter = q;
270 hex_unescape( ludp->lud_filter );
271 }
272 }
273
274 if ( strcasecmp( p, "one" ) == 0 ) {
275 ludp->lud_scope = LDAP_SCOPE_ONELEVEL;
276 } else if ( strcasecmp( p, "base" ) == 0 ) {
277 ludp->lud_scope = LDAP_SCOPE_BASE;
278 } else if ( strcasecmp( p, "sub" ) == 0 ) {
279 ludp->lud_scope = LDAP_SCOPE_SUBTREE;
280 } else if ( *p != '\0' ) {
281 ldap_free_urldesc( ludp );
282 return( LDAP_URL_ERR_BADSCOPE );
283 }
284 }
285 }
286 if ( *ludp->lud_dn == '\0' ) {
287 ludp->lud_dn = NULL;
288 } else {
289 hex_unescape( ludp->lud_dn );
290 }
291
292 /*
293 * if attrs list was included, turn it into a null-terminated array
294 */
295 if ( attrs != NULL && *attrs != '\0' ) {
296 for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) {
297 if ( *p == ',' ) {
298 ++nattrs;
299 }
300 }
301
302 if (( ludp->lud_attrs = (char **)calloc( nattrs + 1,
303 sizeof( char * ))) == NULL ) {
304 ldap_free_urldesc( ludp );
305 return( LDAP_URL_ERR_MEM );
306 }
307
308 for ( i = 0, p = attrs; i < nattrs; ++i ) {
309 ludp->lud_attrs[ i ] = p;
310 if (( p = strchr( p, ',' )) != NULL ) {
311 *p++ ='\0';
312 }
313 hex_unescape( ludp->lud_attrs[ i ] );
314 }
315 }
316
317 if (x != NULL && *x != '\0'){
318 if (errcode = ldap_url_extension_parse(x, &ludp->lud_extensions)){
319 ldap_free_urldesc(ludp);
320 return ( errcode );
321 }
322 }
323 }
324
325 *ludpp = ludp;
326
327 return( 0 );
328 }
329
ldap_free_urlexts(LDAPURLExt ** lues)330 void ldap_free_urlexts( LDAPURLExt ** lues)
331 {
332 int i;
333 for (i = 0; lues[i] != NULL; i++){
334 free(lues[i]);
335 }
336 free(lues);
337 }
338
339
340 void
ldap_free_urldesc(LDAPURLDesc * ludp)341 ldap_free_urldesc( LDAPURLDesc *ludp )
342 {
343 if ( ludp != NULLLDAPURLDESC ) {
344 if ( ludp->lud_string != NULL ) {
345 free( ludp->lud_string );
346 }
347 if ( ludp->lud_attrs != NULL ) {
348 free( ludp->lud_attrs );
349 }
350 if (ludp->lud_extensions != NULL) {
351 ldap_free_urlexts(ludp->lud_extensions);
352 }
353 free( ludp );
354 }
355 }
356
357
358
359 int
ldap_url_search(LDAP * ld,char * url,int attrsonly)360 ldap_url_search( LDAP *ld, char *url, int attrsonly )
361 {
362 int err;
363 LDAPURLDesc *ludp;
364 BerElement *ber;
365 LDAPServer *srv = NULL;
366
367 #ifdef _REENTRANT
368 LOCK_LDAP(ld);
369 #endif
370 if ( ldap_url_parse( url, &ludp ) != 0 ) {
371 ld->ld_errno = LDAP_PARAM_ERROR;
372 #ifdef _REENTRANT
373 UNLOCK_LDAP(ld);
374 #endif
375 return( -1 );
376 }
377
378 if (( ber = ldap_build_search_req( ld, ludp->lud_dn,
379 ludp->lud_scope == LDAP_SCOPE_UNKNOWN ? LDAP_SCOPE_BASE : ludp->lud_scope,
380 ludp->lud_filter ? ludp->lud_filter : "(objectclass=*)",
381 ludp->lud_attrs, attrsonly, NULL, NULL, -1 )) == NULLBER ) {
382 #ifdef _REENTRANT
383 UNLOCK_LDAP(ld);
384 #endif
385 return( -1 );
386 }
387
388 err = 0;
389
390 if ( ludp->lud_host != NULL || ludp->lud_port != 0 ) {
391 if (( srv = (LDAPServer *)calloc( 1, sizeof( LDAPServer )))
392 == NULL || ( srv->lsrv_host = strdup( ludp->lud_host ==
393 NULL ? ld->ld_defhost : ludp->lud_host )) == NULL ) {
394 if ( srv != NULL ) {
395 free( srv );
396 }
397 ld->ld_errno = LDAP_NO_MEMORY;
398 err = -1;
399 } else {
400 if ( ludp->lud_port == 0 ) {
401 srv->lsrv_port = LDAP_PORT;
402 } else {
403 srv->lsrv_port = ludp->lud_port;
404 }
405 }
406 }
407
408 if ( err != 0 ) {
409 ber_free( ber, 1 );
410 } else {
411 err = send_server_request( ld, ber, ld->ld_msgid, NULL, srv, NULL, 1 );
412 }
413
414 ldap_free_urldesc( ludp );
415
416 #ifdef _REENTRANT
417 UNLOCK_LDAP(ld);
418 #endif
419 return( err );
420 }
421
422
423 int
ldap_url_search_st(LDAP * ld,char * url,int attrsonly,struct timeval * timeout,LDAPMessage ** res)424 ldap_url_search_st( LDAP *ld, char *url, int attrsonly,
425 struct timeval *timeout, LDAPMessage **res )
426 {
427 int msgid;
428 int retcode = LDAP_SUCCESS;
429
430 if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
431 return( ld->ld_errno );
432 }
433
434 if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
435 return( ld->ld_errno );
436 }
437
438 if ( ld->ld_errno == LDAP_TIMEOUT ) {
439 (void) ldap_abandon( ld, msgid );
440 ld->ld_errno = LDAP_TIMEOUT;
441 return( ld->ld_errno );
442 }
443
444 #ifdef _REENTRANT
445 LOCK_LDAP(ld);
446 #endif
447 retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched, &ld->ld_error,
448 &ld->ld_referrals, &ld->ld_ret_ctrls, 0);
449 if (retcode == LDAP_SUCCESS)
450 retcode = ld->ld_errno;
451 #ifdef _REENTRANT
452 UNLOCK_LDAP(ld);
453 #endif
454
455 return (retcode);
456 }
457
458
459 int
ldap_url_search_s(LDAP * ld,char * url,int attrsonly,LDAPMessage ** res)460 ldap_url_search_s( LDAP *ld, char *url, int attrsonly, LDAPMessage **res )
461 {
462 int msgid;
463 int retcode = LDAP_SUCCESS;
464
465 if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
466 return( ld->ld_errno );
467 }
468
469 if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
470 return( ld->ld_errno );
471 }
472
473 #ifdef _REENTRANT
474 LOCK_LDAP(ld);
475 #endif
476 retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched, &ld->ld_error,
477 &ld->ld_referrals, &ld->ld_ret_ctrls, 0);
478 if (retcode == LDAP_SUCCESS)
479 retcode = ld->ld_errno;
480 #ifdef _REENTRANT
481 UNLOCK_LDAP(ld);
482 #endif
483
484 return (retcode);
485 }
486
487
488 static void
hex_unescape(char * s)489 hex_unescape( char *s )
490 {
491 /*
492 * Remove URL hex escapes from s... done in place. The basic concept for
493 * this routine is borrowed from the WWW library HTUnEscape() routine.
494 */
495 char *p;
496
497 for ( p = s; *s != '\0'; ++s ) {
498 if ( *s == '%' ) {
499 if ( *++s != '\0' ) {
500 *p = unhex( *s ) << 4;
501 }
502 if ( *++s != '\0' ) {
503 *p++ += unhex( *s );
504 }
505 } else {
506 *p++ = *s;
507 }
508 }
509
510 *p = '\0';
511 }
512
513
514 static int
unhex(char c)515 unhex( char c )
516 {
517 return( c >= '0' && c <= '9' ? c - '0'
518 : c >= 'A' && c <= 'F' ? c - 'A' + 10
519 : c - 'a' + 10 );
520 }
521
522
523 /*
524 * Locate the LDAP URL associated with a DNS domain name.
525 *
526 * The supplied DNS domain name is converted into a distinguished
527 * name. The directory entry specified by that distinguished name
528 * is searched for a labeledURI attribute. If successful then the
529 * LDAP URL is returned. If unsuccessful then that entry's parent
530 * is searched and so on until the target distinguished name is
531 * reduced to only two nameparts.
532 *
533 * For example, if 'ny.eng.wiz.com' is the DNS domain then the
534 * following entries are searched until one succeeds:
535 * dc=ny,dc=eng,dc=wiz,dc=com
536 * dc=eng,dc=wiz,dc=com
537 * dc=wiz,dc=com
538 *
539 * If dns_name is NULL then the environment variable LOCALDOMAIN is used.
540 * If attrs is not NULL then it is appended to the URL's attribute list.
541 * If scope is not NULL then it overrides the URL's scope.
542 * If filter is not NULL then it is merged with the URL's filter.
543 *
544 * If an error is encountered then zero is returned, otherwise a string
545 * URL is returned. The caller should free the returned string if it is
546 * non-zero.
547 */
548
549 char *
ldap_dns_to_url(LDAP * ld,char * dns_name,char * attrs,char * scope,char * filter)550 ldap_dns_to_url(
551 LDAP *ld,
552 char *dns_name,
553 char *attrs,
554 char *scope,
555 char *filter
556 )
557 {
558 char *dn;
559 char *url = 0;
560 char *url2 = 0;
561 LDAPURLDesc *urldesc;
562 char *cp;
563 char *cp2;
564 size_t attrs_len = 0;
565 size_t scope_len = 0;
566 size_t filter_len = 0;
567 int nameparts;
568 int no_attrs = 0;
569 int no_scope = 0;
570
571 if (dns_name == 0) {
572 dns_name = (char *)getenv("LOCALDOMAIN");
573 }
574
575 if ((ld == NULL) || ((dn = ldap_dns_to_dn(dns_name, &nameparts)) ==
576 NULL))
577 return (0);
578
579 if ((url = ldap_dn_to_url(ld, dn, nameparts)) == NULL) {
580 free(dn);
581 return (0);
582 }
583 free(dn);
584
585 /* merge filter and/or scope and/or attributes with URL */
586 if (attrs || scope || filter) {
587
588 if (attrs)
589 attrs_len = strlen(attrs) + 2; /* for comma and NULL */
590
591 if (scope)
592 scope_len = strlen(scope) + 1; /* for NULL */
593
594 if (filter)
595 filter_len = strlen(filter) + 4;
596 /* for ampersand, parentheses and NULL */
597
598 if (ldap_is_ldap_url(url)) {
599
600 if ((url2 = (char *)malloc(attrs_len + scope_len +
601 filter_len + strlen(url) + 1)) == NULL) {
602 return (0);
603 }
604 cp = url;
605 cp2 = url2;
606
607 /* copy URL scheme, hostname, port number and DN */
608 while (*cp && (*cp != '?')) {
609 *cp2++ = *cp++;
610 }
611
612 /* handle URL attributes */
613
614 if (*cp == '?') { /* test first '?' */
615 *cp2++ = *cp++; /* copy first '?' */
616
617 if (*cp == '?') { /* test second '?' */
618
619 /* insert supplied attributes */
620 if (attrs) {
621 while (*attrs) {
622 *cp2++ = *attrs++;
623 }
624 } else {
625 no_attrs = 1;
626 }
627
628 } else {
629
630 /* copy URL attributes */
631 while (*cp && (*cp != '?')) {
632 *cp2++ = *cp++;
633 }
634
635 /* append supplied attributes */
636 if (attrs) {
637 *cp2++ = ',';
638 while (*attrs) {
639 *cp2++ = *attrs++;
640 }
641 }
642 }
643
644 } else {
645 /* append supplied attributes */
646 if (attrs) {
647 *cp2++ = '?';
648 while (*attrs) {
649 *cp2++ = *attrs++;
650 }
651 } else {
652 no_attrs = 1;
653 }
654 }
655
656 /* handle URL scope */
657
658 if (*cp == '?') { /* test second '?' */
659 *cp2++ = *cp++; /* copy second '?' */
660
661 if (*cp == '?') { /* test third '?' */
662
663 /* insert supplied scope */
664 if (scope) {
665 while (*scope) {
666 *cp2++ = *scope++;
667 }
668 } else {
669 no_scope = 1;
670 }
671
672 } else {
673
674 if (scope) {
675 /* skip over URL scope */
676 while (*cp && (*cp != '?')) {
677 *cp++;
678 }
679 /* insert supplied scope */
680 while (*scope) {
681 *cp2++ = *scope++;
682 }
683 } else {
684
685 /* copy URL scope */
686 while (*cp && (*cp != '?')) {
687 *cp2++ = *cp++;
688 }
689 }
690 }
691
692 } else {
693 /* append supplied scope */
694 if (scope) {
695 if (no_attrs) {
696 *cp2++ = '?';
697 }
698 *cp2++ = '?';
699 while (*scope) {
700 *cp2++ = *scope++;
701 }
702 } else {
703 no_scope = 1;
704 }
705 }
706
707 /* handle URL filter */
708
709 if (*cp == '?') { /* test third '?' */
710 *cp2++ = *cp++; /* copy third '?' */
711
712 if (filter) {
713
714 /* merge URL and supplied filters */
715
716 *cp2++ = '(';
717 *cp2++ = '&';
718 /* copy URL filter */
719 while (*cp) {
720 *cp2++ = *cp++;
721 }
722 /* append supplied filter */
723 while (*filter) {
724 *cp2++ = *filter++;
725 }
726 *cp2++ = ')';
727 } else {
728
729 /* copy URL filter */
730 while (*cp) {
731 *cp2++ = *cp++;
732 }
733 }
734
735 } else {
736 /* append supplied filter */
737 if (filter) {
738 if (no_scope) {
739 if (no_attrs) {
740 *cp2++ = '?';
741 }
742 *cp2++ = '?';
743 }
744 *cp2++ = '?';
745 while (*filter) {
746 *cp2++ = *filter++;
747 }
748 }
749 }
750
751 *cp2++ = '\0';
752 free (url);
753 url = url2;
754
755 } else {
756 return (0); /* not an LDAP URL */
757 }
758 }
759 return (url);
760 }
761
762
763 /*
764 * Locate the LDAP URL associated with a distinguished name.
765 *
766 * The number of nameparts in the supplied distinguished name must be
767 * provided. The specified directory entry is searched for a labeledURI
768 * attribute. If successful then the LDAP URL is returned. If unsuccessful
769 * then that entry's parent is searched and so on until the target
770 * distinguished name is reduced to only two nameparts.
771 *
772 * For example, if 'l=ny,ou=eng,o=wiz,c=us' is the distinguished name
773 * then the following entries are searched until one succeeds:
774 * l=ny,ou=eng,o=wiz,c=us
775 * ou=eng,o=wiz,c=us
776 * o=wiz,c=us
777 *
778 * If an error is encountered then zero is returned, otherwise a string
779 * URL is returned. The caller should free the returned string if it is
780 * non-zero.
781 */
782
783 char *
ldap_dn_to_url(LDAP * ld,char * dn,int nameparts)784 ldap_dn_to_url(
785 LDAP *ld,
786 char *dn,
787 int nameparts
788 )
789 {
790 char *next_dn = dn;
791 char *url = 0;
792 char *attrs[2] = {"labeledURI", 0};
793 LDAPMessage *res, *e;
794 char **vals;
795
796 /*
797 * Search for a URL in the named entry or its parent entry.
798 * Continue until only 2 nameparts remain.
799 */
800 while (dn && (nameparts > 1) && (! url)) {
801
802 /* search for the labeledURI attribute */
803 if (ldap_search_s(ld, dn, LDAP_SCOPE_BASE,
804 "(objectClass=*)", attrs, 0, &res) == LDAP_SUCCESS) {
805
806 /* locate the first entry returned */
807 if ((e = ldap_first_entry(ld, res)) != NULL) {
808
809 /* locate the labeledURI attribute */
810 if ((vals =
811 ldap_get_values(ld, e, "labeledURI")) !=
812 NULL) {
813
814 /* copy the attribute value */
815 if ((url = strdup((char *)vals[0])) !=
816 NULL) {
817 ldap_value_free(vals);
818 }
819 }
820 }
821 /* free the search results */
822 ldap_msgfree(res);
823 }
824
825 if (! url) {
826 /* advance along the DN by one namepart */
827 if (next_dn = strchr(dn, ',')) {
828 next_dn++;
829 dn = next_dn;
830 nameparts--;
831 }
832 }
833 }
834
835 return (url);
836 }
837