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