]> git.sur5r.net Git - openldap/blob - libraries/libldap/url.c
ccb7eca61335ca6267887896b1fc4b84be3467d0
[openldap] / libraries / libldap / url.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1996 Regents of the University of Michigan.
8  *  All rights reserved.
9  *
10  *  LIBLDAP url.c -- LDAP URL (RFC 2255) related routines
11  *
12  *  LDAP URLs look like this:
13  *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
14  *
15  *  where:
16  *   attributes is a comma separated list
17  *   scope is one of these three strings:  base one sub (default=base)
18  *   filter is an string-represented filter as in RFC 2254
19  *
20  *  e.g.,  ldap://host:port/dc=com?o,cn?base?o=openldap?extension
21  *
22  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
23  */
24
25 #include "portable.h"
26
27 #include <stdio.h>
28
29 #include <ac/stdlib.h>
30
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((
40         const char *url,
41         int *enclosedp,
42         const char **scheme ));
43
44 int ldap_pvt_url_scheme2proto( const char *scheme )
45 {
46         assert( scheme );
47
48         if( scheme == NULL ) {
49                 return -1;
50         }
51
52         if( strcmp("ldap", scheme) == 0 ) {
53                 return LDAP_PROTO_TCP;
54         }
55
56         if( strcmp("ldapi", scheme) == 0 ) {
57                 return LDAP_PROTO_IPC;
58         }
59
60         if( strcmp("ldaps", scheme) == 0 ) {
61                 return LDAP_PROTO_TCP;
62         }
63
64         return -1;
65 }
66
67 int ldap_pvt_url_scheme2tls( const char *scheme )
68 {
69         assert( scheme );
70
71         if( scheme == NULL ) {
72                 return -1;
73         }
74
75         return strcmp("ldaps", scheme) == 0;
76 }
77
78 int
79 ldap_is_ldap_url( LDAP_CONST char *url )
80 {
81         int     enclosed;
82         const char * scheme;
83
84         if( url == NULL ) {
85                 return 0;
86         }
87
88         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
89                 return 0;
90         }
91
92         return 1;
93 }
94
95 int
96 ldap_is_ldaps_url( LDAP_CONST char *url )
97 {
98         int     enclosed;
99         const char * scheme;
100
101         if( url == NULL ) {
102                 return 0;
103         }
104
105         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
106                 return 0;
107         }
108
109         return strcmp(scheme, "ldaps") == 0;
110 }
111
112 int
113 ldap_is_ldapi_url( LDAP_CONST char *url )
114 {
115         int     enclosed;
116         const char * scheme;
117
118         if( url == NULL ) {
119                 return 0;
120         }
121
122         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
123                 return 0;
124         }
125
126         return strcmp(scheme, "ldapi") == 0;
127 }
128
129 static const char*
130 skip_url_prefix(
131         const char *url,
132         int *enclosedp,
133         const char **scheme )
134 {
135 /*
136  * return non-zero if this looks like a LDAP URL; zero if not
137  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
138  */
139         const char *p;
140
141         if ( url == NULL ) {
142                 return( NULL );
143         }
144
145         p = url;
146
147         /* skip leading '<' (if any) */
148         if ( *p == '<' ) {
149                 *enclosedp = 1;
150                 ++p;
151         } else {
152                 *enclosedp = 0;
153         }
154
155         /* skip leading "URL:" (if any) */
156         if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 )
157         {
158                 p += LDAP_URL_URLCOLON_LEN;
159         }
160
161         /* check for "ldap://" prefix */
162         if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
163                 /* skip over "ldap://" prefix and return success */
164                 p += LDAP_URL_PREFIX_LEN;
165                 *scheme = "ldap";
166                 return( p );
167         }
168
169         /* check for "ldaps://" prefix */
170         if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
171                 /* skip over "ldaps://" prefix and return success */
172                 p += LDAPS_URL_PREFIX_LEN;
173                 *scheme = "ldaps";
174                 return( p );
175         }
176
177         /* check for "ldapi://" prefix */
178         if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
179                 /* skip over "ldapi://" prefix and return success */
180                 p += LDAPI_URL_PREFIX_LEN;
181                 *scheme = "ldapi";
182                 return( p );
183         }
184
185         return( NULL );
186 }
187
188
189 static int str2scope( const char *p )
190 {
191         if ( strcasecmp( p, "one" ) == 0 ) {
192                 return LDAP_SCOPE_ONELEVEL;
193
194         } else if ( strcasecmp( p, "onetree" ) == 0 ) {
195                 return LDAP_SCOPE_ONELEVEL;
196
197         } else if ( strcasecmp( p, "base" ) == 0 ) {
198                 return LDAP_SCOPE_BASE;
199
200         } else if ( strcasecmp( p, "sub" ) == 0 ) {
201                 return LDAP_SCOPE_SUBTREE;
202
203         } else if ( strcasecmp( p, "subtree" ) == 0 ) {
204                 return LDAP_SCOPE_SUBTREE;
205         }
206
207         return( -1 );
208 }
209
210
211 int
212 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
213 {
214 /*
215  *  Pick apart the pieces of an LDAP URL.
216  */
217
218         LDAPURLDesc     *ludp;
219         char    *p, *q, *r;
220         int             i, enclosed;
221         const char *scheme = NULL;
222         const char *url_tmp;
223         char *url;
224
225         if( url_in == NULL && ludpp == NULL ) {
226                 return LDAP_URL_ERR_PARAM;
227         }
228
229 #ifndef LDAP_INT_IN_KERNEL
230         /* Global options may not be created yet
231          * We can't test if the global options are initialized
232          * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
233          * the options and cause infinite recursion
234          */
235         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse(%s)\n", url_in, 0, 0 );
236 #endif
237
238         *ludpp = NULL;  /* pessimistic */
239
240         url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
241
242         if ( url_tmp == NULL ) {
243                 return LDAP_URL_ERR_BADSCHEME;
244         }
245
246         assert( scheme );
247
248         /* make working copy of the remainder of the URL */
249         url = LDAP_STRDUP( url_tmp );
250         if ( url == NULL ) {
251                 return LDAP_URL_ERR_MEM;
252         }
253
254         if ( enclosed ) {
255                 p = &url[strlen(url)-1];
256
257                 if( *p != '>' ) {
258                         LDAP_FREE( url );
259                         return LDAP_URL_ERR_BADENCLOSURE;
260                 }
261
262                 *p = '\0';
263         }
264
265         /* allocate return struct */
266         ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
267
268         if ( ludp == NULL ) {
269                 LDAP_FREE( url );
270                 return LDAP_URL_ERR_MEM;
271         }
272
273         ludp->lud_next = NULL;
274         ludp->lud_host = NULL;
275         ludp->lud_port = LDAP_PORT;
276         ludp->lud_dn = NULL;
277         ludp->lud_attrs = NULL;
278         ludp->lud_filter = NULL;
279         ludp->lud_scope = LDAP_SCOPE_BASE;
280         ludp->lud_filter = NULL;
281
282         ludp->lud_scheme = LDAP_STRDUP( scheme );
283
284         if ( ludp->lud_scheme == NULL ) {
285                 LDAP_FREE( url );
286                 ldap_free_urldesc( ludp );
287                 return LDAP_URL_ERR_MEM;
288         }
289
290         if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
291                 ludp->lud_port = LDAPS_PORT;
292         }
293
294         /* scan forward for '/' that marks end of hostport and begin. of dn */
295         p = strchr( url, '/' );
296
297         if( p != NULL ) {
298                 /* terminate hostport; point to start of dn */
299                 *p++ = '\0';
300         }
301
302         /* IPv6 syntax with [ip address]:port */
303         if ( *url == '[' ) {
304                 r = strchr( url, ']' );
305                 if ( r == NULL ) {
306                         LDAP_FREE( url );
307                         ldap_free_urldesc( ludp );
308                         return LDAP_URL_ERR_BADURL;
309                 }
310                 *r++ = '\0';
311                 q = strchr( r, ':' );
312         } else {
313                 q = strchr( url, ':' );
314         }
315
316         if ( q != NULL ) {
317                 *q++ = '\0';
318                 ldap_pvt_hex_unescape( q );
319
320                 if( *q == '\0' ) {
321                         LDAP_FREE( url );
322                         ldap_free_urldesc( ludp );
323                         return LDAP_URL_ERR_BADURL;
324                 }
325
326                 ludp->lud_port = atoi( q );
327         }
328
329         ldap_pvt_hex_unescape( url );
330
331         /* If [ip address]:port syntax, url is [ip and we skip the [ */
332         ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
333
334         if( ludp->lud_host == NULL ) {
335                 LDAP_FREE( url );
336                 ldap_free_urldesc( ludp );
337                 return LDAP_URL_ERR_MEM;
338         }
339
340         /*
341          * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
342          *
343          * On early Novell releases, search references/referrals were returned
344          * in this format, i.e., the dn was kind of in the scope position,
345          * but the required slash is missing. The whole thing is illegal syntax,
346          * but we need to account for it. Fortunately it can't be confused with
347          * anything real.
348          */
349         if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
350                 q++;            
351                 /* ? immediately followed by question */
352                 if( *q == '?') {
353                         q++;
354                         if( *q != '\0' ) {
355                                 /* parse dn part */
356                                 ldap_pvt_hex_unescape( q );
357                                 ludp->lud_dn = LDAP_STRDUP( q );
358                         } else {
359                                 ludp->lud_dn = LDAP_STRDUP( "" );
360                         }
361
362                         if( ludp->lud_dn == NULL ) {
363                                 LDAP_FREE( url );
364                                 ldap_free_urldesc( ludp );
365                                 return LDAP_URL_ERR_MEM;
366                         }
367                 }
368         }
369
370         if( p == NULL ) {
371                 LDAP_FREE( url );
372                 *ludpp = ludp;
373                 return LDAP_URL_SUCCESS;
374         }
375
376         /* scan forward for '?' that may marks end of dn */
377         q = strchr( p, '?' );
378
379         if( q != NULL ) {
380                 /* terminate dn part */
381                 *q++ = '\0';
382         }
383
384         if( *p != '\0' ) {
385                 /* parse dn part */
386                 ldap_pvt_hex_unescape( p );
387                 ludp->lud_dn = LDAP_STRDUP( p );
388         } else {
389                 ludp->lud_dn = LDAP_STRDUP( "" );
390         }
391
392         if( ludp->lud_dn == NULL ) {
393                 LDAP_FREE( url );
394                 ldap_free_urldesc( ludp );
395                 return LDAP_URL_ERR_MEM;
396         }
397
398         if( q == NULL ) {
399                 /* no more */
400                 LDAP_FREE( url );
401                 *ludpp = ludp;
402                 return LDAP_URL_SUCCESS;
403         }
404
405         /* scan forward for '?' that may marks end of attributes */
406         p = q;
407         q = strchr( p, '?' );
408
409         if( q != NULL ) {
410                 /* terminate attributes part */
411                 *q++ = '\0';
412         }
413
414         if( *p != '\0' ) {
415                 /* parse attributes */
416                 ldap_pvt_hex_unescape( p );
417                 ludp->lud_attrs = ldap_str2charray( p, "," );
418
419                 if( ludp->lud_attrs == NULL ) {
420                         LDAP_FREE( url );
421                         ldap_free_urldesc( ludp );
422                         return LDAP_URL_ERR_BADATTRS;
423                 }
424         }
425
426         if ( q == NULL ) {
427                 /* no more */
428                 LDAP_FREE( url );
429                 *ludpp = ludp;
430                 return LDAP_URL_SUCCESS;
431         }
432
433         /* scan forward for '?' that may marks end of scope */
434         p = q;
435         q = strchr( p, '?' );
436
437         if( q != NULL ) {
438                 /* terminate the scope part */
439                 *q++ = '\0';
440         }
441
442         if( *p != '\0' ) {
443                 /* parse the scope */
444                 ldap_pvt_hex_unescape( p );
445                 ludp->lud_scope = str2scope( p );
446
447                 if( ludp->lud_scope == -1 ) {
448                         LDAP_FREE( url );
449                         ldap_free_urldesc( ludp );
450                         return LDAP_URL_ERR_BADSCOPE;
451                 }
452         }
453
454         if ( q == NULL ) {
455                 /* no more */
456                 LDAP_FREE( url );
457                 *ludpp = ludp;
458                 return LDAP_URL_SUCCESS;
459         }
460
461         /* scan forward for '?' that may marks end of filter */
462         p = q;
463         q = strchr( p, '?' );
464
465         if( q != NULL ) {
466                 /* terminate the filter part */
467                 *q++ = '\0';
468         }
469
470         if( *p != '\0' ) {
471                 /* parse the filter */
472                 ldap_pvt_hex_unescape( p );
473
474                 if( ! *p ) {
475                         /* missing filter */
476                         LDAP_FREE( url );
477                         ldap_free_urldesc( ludp );
478                         return LDAP_URL_ERR_BADFILTER;
479                 }
480
481                 LDAP_FREE( ludp->lud_filter );
482                 ludp->lud_filter = LDAP_STRDUP( p );
483
484                 if( ludp->lud_filter == NULL ) {
485                         LDAP_FREE( url );
486                         ldap_free_urldesc( ludp );
487                         return LDAP_URL_ERR_MEM;
488                 }
489         }
490
491         if ( q == NULL ) {
492                 /* no more */
493                 LDAP_FREE( url );
494                 *ludpp = ludp;
495                 return LDAP_URL_SUCCESS;
496         }
497
498         /* scan forward for '?' that may marks end of extensions */
499         p = q;
500         q = strchr( p, '?' );
501
502         if( q != NULL ) {
503                 /* extra '?' */
504                 LDAP_FREE( url );
505                 ldap_free_urldesc( ludp );
506                 return LDAP_URL_ERR_BADURL;
507         }
508
509         /* parse the extensions */
510         ludp->lud_exts = ldap_str2charray( p, "," );
511
512         if( ludp->lud_exts == NULL ) {
513                 LDAP_FREE( url );
514                 ldap_free_urldesc( ludp );
515                 return LDAP_URL_ERR_BADEXTS;
516         }
517
518         for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
519                 ldap_pvt_hex_unescape( ludp->lud_exts[i] );
520         }
521
522         if( i == 0 ) {
523                 /* must have 1 or more */
524                 ldap_charray_free( ludp->lud_exts );
525                 LDAP_FREE( url );
526                 ldap_free_urldesc( ludp );
527                 return LDAP_URL_ERR_BADEXTS;
528         }
529
530         /* no more */
531         *ludpp = ludp;
532         LDAP_FREE( url );
533         return LDAP_URL_SUCCESS;
534 }
535
536 LDAPURLDesc *
537 ldap_url_dup ( LDAPURLDesc *ludp )
538 {
539         LDAPURLDesc *dest;
540
541         if ( ludp == NULL ) {
542                 return NULL;
543         }
544
545         dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
546         if (dest == NULL)
547                 return NULL;
548         
549         *dest = *ludp;
550         dest->lud_scheme = NULL;
551         dest->lud_host = NULL;
552         dest->lud_dn = NULL;
553         dest->lud_filter = NULL;
554         dest->lud_attrs = NULL;
555         dest->lud_exts = NULL;
556         dest->lud_next = NULL;
557
558         if ( ludp->lud_scheme != NULL ) {
559                 dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
560                 if (dest->lud_scheme == NULL) {
561                         ldap_free_urldesc(dest);
562                         return NULL;
563                 }
564         }
565
566         if ( ludp->lud_host != NULL ) {
567                 dest->lud_host = LDAP_STRDUP( ludp->lud_host );
568                 if (dest->lud_host == NULL) {
569                         ldap_free_urldesc(dest);
570                         return NULL;
571                 }
572         }
573
574         if ( ludp->lud_dn != NULL ) {
575                 dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
576                 if (dest->lud_dn == NULL) {
577                         ldap_free_urldesc(dest);
578                         return NULL;
579                 }
580         }
581
582         if ( ludp->lud_filter != NULL ) {
583                 dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
584                 if (dest->lud_filter == NULL) {
585                         ldap_free_urldesc(dest);
586                         return NULL;
587                 }
588         }
589
590         if ( ludp->lud_attrs != NULL ) {
591                 dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
592                 if (dest->lud_attrs == NULL) {
593                         ldap_free_urldesc(dest);
594                         return NULL;
595                 }
596         }
597
598         if ( ludp->lud_exts != NULL ) {
599                 dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
600                 if (dest->lud_exts == NULL) {
601                         ldap_free_urldesc(dest);
602                         return NULL;
603                 }
604         }
605
606         return dest;
607 }
608
609 LDAPURLDesc *
610 ldap_url_duplist (LDAPURLDesc *ludlist)
611 {
612         LDAPURLDesc *dest, *tail, *ludp, *newludp;
613
614         dest = NULL;
615         tail = NULL;
616         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
617                 newludp = ldap_url_dup(ludp);
618                 if (newludp == NULL) {
619                         ldap_free_urllist(dest);
620                         return NULL;
621                 }
622                 if (tail == NULL)
623                         dest = newludp;
624                 else
625                         tail->lud_next = newludp;
626                 tail = newludp;
627         }
628         return dest;
629 }
630
631 int
632 ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
633 {
634         int i, rc;
635         LDAPURLDesc *ludp;
636         char **urls;
637
638         *ludlist = NULL;
639
640         if (url == NULL)
641                 return LDAP_PARAM_ERROR;
642
643         urls = ldap_str2charray((char *)url, ", ");
644         if (urls == NULL)
645                 return LDAP_NO_MEMORY;
646
647         /* count the URLs... */
648         for (i = 0; urls[i] != NULL; i++) ;
649         /* ...and put them in the "stack" backward */
650         while (--i >= 0) {
651                 rc = ldap_url_parse( urls[i], &ludp );
652                 if ( rc != 0 ) {
653                         ldap_charray_free(urls);
654                         ldap_free_urllist(*ludlist);
655                         *ludlist = NULL;
656                         return rc;
657                 }
658                 ludp->lud_next = *ludlist;
659                 *ludlist = ludp;
660         }
661         ldap_charray_free(urls);
662         return LDAP_SUCCESS;
663 }
664
665 int
666 ldap_url_parsehosts(
667         LDAPURLDesc **ludlist,
668         const char *hosts,
669         int port )
670 {
671         int i;
672         LDAPURLDesc *ludp;
673         char **specs, *p;
674
675         *ludlist = NULL;
676
677         if (hosts == NULL)
678                 return LDAP_PARAM_ERROR;
679
680         specs = ldap_str2charray((char *)hosts, ", ");
681         if (specs == NULL)
682                 return LDAP_NO_MEMORY;
683
684         /* count the URLs... */
685         for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
686
687         /* ...and put them in the "stack" backward */
688         while (--i >= 0) {
689                 ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
690                 if (ludp == NULL) {
691                         ldap_charray_free(specs);
692                         ldap_free_urllist(*ludlist);
693                         *ludlist = NULL;
694                         return LDAP_NO_MEMORY;
695                 }
696                 ludp->lud_port = port;
697                 ludp->lud_host = specs[i];
698                 specs[i] = NULL;
699                 p = strchr(ludp->lud_host, ':');
700                 if (p != NULL) {
701                         /* more than one :, IPv6 address */
702                         if ( strchr(p+1, ':') != NULL ) {
703                                 /* allow [address] and [address]:port */
704                                 if ( *ludp->lud_host == '[' ) {
705                                         p = LDAP_STRDUP(ludp->lud_host+1);
706                                         /* copied, make sure we free source later */
707                                         specs[i] = ludp->lud_host;
708                                         ludp->lud_host = p;
709                                         p = strchr( ludp->lud_host, ']' );
710                                         if ( p == NULL )
711                                                 return LDAP_PARAM_ERROR;
712                                         *p++ = '\0';
713                                         if ( *p != ':' ) {
714                                                 if ( *p != '\0' )
715                                                         return LDAP_PARAM_ERROR;
716                                                 p = NULL;
717                                         }
718                                 } else {
719                                         p = NULL;
720                                 }
721                         }
722                         if (p != NULL) {
723                                 *p++ = 0;
724                                 ldap_pvt_hex_unescape(p);
725                                 ludp->lud_port = atoi(p);
726                         }
727                 }
728                 ldap_pvt_hex_unescape(ludp->lud_host);
729                 ludp->lud_scheme = LDAP_STRDUP("ldap");
730                 ludp->lud_next = *ludlist;
731                 *ludlist = ludp;
732         }
733
734         /* this should be an array of NULLs now */
735         /* except entries starting with [ */
736         ldap_charray_free(specs);
737         return LDAP_SUCCESS;
738 }
739
740 char *
741 ldap_url_list2hosts (LDAPURLDesc *ludlist)
742 {
743         LDAPURLDesc *ludp;
744         int size;
745         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
746
747         if (ludlist == NULL)
748                 return NULL;
749
750         /* figure out how big the string is */
751         size = 1;       /* nul-term */
752         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
753                 size += strlen(ludp->lud_host) + 1;             /* host and space */
754                 if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
755                         size += 2;
756                 if (ludp->lud_port != 0)
757                         size += sprintf(buf, ":%d", ludp->lud_port);
758         }
759         s = LDAP_MALLOC(size);
760         if (s == NULL)
761                 return NULL;
762
763         p = s;
764         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
765                 if (strchr(ludp->lud_host, ':')) {
766                         p += sprintf(p, "[%s]", ludp->lud_host);
767                 } else {
768                         strcpy(p, ludp->lud_host);
769                         p += strlen(ludp->lud_host);
770                 }
771                 if (ludp->lud_port != 0)
772                         p += sprintf(p, ":%d", ludp->lud_port);
773                 *p++ = ' ';
774         }
775         if (p != s)
776                 p--;    /* nuke that extra space */
777         *p = 0;
778         return s;
779 }
780
781 char *
782 ldap_url_list2urls(
783         LDAPURLDesc *ludlist )
784 {
785         LDAPURLDesc *ludp;
786         int size;
787         char *s, *p, buf[32];   /* big enough to hold a long decimal # (overkill) */
788
789         if (ludlist == NULL)
790                 return NULL;
791
792         /* figure out how big the string is */
793         size = 1;       /* nul-term */
794         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
795                 size += strlen(ludp->lud_scheme) + strlen(ludp->lud_host);
796                 if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
797                         size += 2;
798                 size += sizeof(":/// ");
799
800                 if (ludp->lud_port != 0) {
801                         size += sprintf(buf, ":%d", ludp->lud_port);
802                 }
803         }
804
805         s = LDAP_MALLOC(size);
806         if (s == NULL) {
807                 return NULL;
808         }
809
810         p = s;
811         for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
812                 p += sprintf(p,
813                              strchr(ludp->lud_host, ':') ? "%s://[%s]" : "%s://%s",
814                              ludp->lud_scheme, ludp->lud_host);
815                 if (ludp->lud_port != 0)
816                         p += sprintf(p, ":%d", ludp->lud_port);
817                 *p++ = '/';
818                 *p++ = ' ';
819         }
820         if (p != s)
821                 p--;    /* nuke that extra space */
822         *p = 0;
823         return s;
824 }
825
826 void
827 ldap_free_urllist( LDAPURLDesc *ludlist )
828 {
829         LDAPURLDesc *ludp, *next;
830
831         for (ludp = ludlist; ludp != NULL; ludp = next) {
832                 next = ludp->lud_next;
833                 ldap_free_urldesc(ludp);
834         }
835 }
836
837 void
838 ldap_free_urldesc( LDAPURLDesc *ludp )
839 {
840         if ( ludp == NULL ) {
841                 return;
842         }
843         
844         if ( ludp->lud_scheme != NULL ) {
845                 LDAP_FREE( ludp->lud_scheme );
846         }
847
848         if ( ludp->lud_host != NULL ) {
849                 LDAP_FREE( ludp->lud_host );
850         }
851
852         if ( ludp->lud_dn != NULL ) {
853                 LDAP_FREE( ludp->lud_dn );
854         }
855
856         if ( ludp->lud_filter != NULL ) {
857                 LDAP_FREE( ludp->lud_filter);
858         }
859
860         if ( ludp->lud_attrs != NULL ) {
861                 LDAP_VFREE( ludp->lud_attrs );
862         }
863
864         if ( ludp->lud_exts != NULL ) {
865                 LDAP_VFREE( ludp->lud_exts );
866         }
867
868         LDAP_FREE( ludp );
869 }
870
871
872
873 int
874 ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
875 {
876         int             err;
877         LDAPURLDesc     *ludp;
878         BerElement      *ber;
879         LDAPreqinfo  bind;
880
881         if ( ldap_url_parse( url, &ludp ) != 0 ) {
882                 ld->ld_errno = LDAP_PARAM_ERROR;
883                 return( -1 );
884         }
885
886         ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
887             ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
888                 -1, -1 );
889
890         if ( ber == NULL ) {
891                 err = -1;
892         } else {
893                 bind.ri_request = LDAP_REQ_SEARCH;
894                 bind.ri_msgid = ld->ld_msgid;
895                 bind.ri_url = (char *)url;
896                 err = ldap_send_server_request(
897                                         ld, ber, ld->ld_msgid, NULL,
898                                         (ludp->lud_host != NULL || ludp->lud_port != 0)
899                                                 ? ludp : NULL,
900                                         NULL, &bind );
901         }
902
903         ldap_free_urldesc( ludp );
904         return( err );
905 }
906
907
908 int
909 ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
910         struct timeval *timeout, LDAPMessage **res )
911 {
912         int     msgid;
913
914         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
915                 return( ld->ld_errno );
916         }
917
918         if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
919                 return( ld->ld_errno );
920         }
921
922         if ( ld->ld_errno == LDAP_TIMEOUT ) {
923                 (void) ldap_abandon( ld, msgid );
924                 ld->ld_errno = LDAP_TIMEOUT;
925                 return( ld->ld_errno );
926         }
927
928         return( ldap_result2error( ld, *res, 0 ));
929 }
930
931
932 int
933 ldap_url_search_s(
934         LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
935 {
936         int     msgid;
937
938         if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
939                 return( ld->ld_errno );
940         }
941
942         if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
943                 return( ld->ld_errno );
944         }
945
946         return( ldap_result2error( ld, *res, 0 ));
947 }
948
949
950 void
951 ldap_pvt_hex_unescape( char *s )
952 {
953 /*
954 * Remove URL hex escapes from s... done in place.  The basic concept for
955 * this routine is borrowed from the WWW library HTUnEscape() routine.
956 */
957         char    *p;
958
959         for ( p = s; *s != '\0'; ++s ) {
960                 if ( *s == '%' ) {
961                         if ( *++s != '\0' ) {
962                                 *p = ldap_pvt_unhex( *s ) << 4;
963                         }
964                         if ( *++s != '\0' ) {
965                                 *p++ += ldap_pvt_unhex( *s );
966                         }
967                 } else {
968                         *p++ = *s;
969                 }
970         }
971
972         *p = '\0';
973 }
974
975
976 int
977 ldap_pvt_unhex( int c )
978 {
979         return( c >= '0' && c <= '9' ? c - '0'
980             : c >= 'A' && c <= 'F' ? c - 'A' + 10
981             : c - 'a' + 10 );
982 }