]> git.sur5r.net Git - openldap/blob - libraries/libldap/ufn.c
Update dn2domain to use str2dn() not explode_dn()
[openldap] / libraries / libldap / ufn.c
1 /*
2  *  Copyright (c) 1990 Regents of the University of Michigan.
3  *  All rights reserved.
4  *
5  *  ufn.c
6  */
7
8 #ifndef lint 
9 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15
16 #ifdef MACOS
17 #include <stdlib.h>
18 #include "macos.h"
19 #else /* MACOS */
20 #if defined( DOS ) || defined( _WIN32 )
21 #include "msdos.h"
22 #else /* DOS */
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #endif /* DOS */
27 #endif /* MACOS */
28
29 #include "lber.h"
30 #include "ldap.h"
31
32 #ifdef NEEDPROTOS
33 typedef int (*cancelptype)( void *cancelparm );
34 #else /* NEEDPROTOS */
35 typedef int (*cancelptype)();
36 #endif /* NEEDPROTOS */
37
38 #ifdef NEEDPROTOS
39 static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, 
40         char *prefix, char **attrs, int attrsonly, LDAPMessage **res, 
41         cancelptype cancelproc, void *cancelparm, char *tag1, char *tag2,
42         char *tag3 );
43 static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
44 static LDAPMessage *ldap_ufn_expand( LDAP *ld, cancelptype cancelproc,
45         void *cancelparm, char **dns, char *filter, int scope,
46         char **attrs, int aonly, int *err );
47 LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
48 #else /* NEEDPROTOS */
49 static LDAPMessage *ldap_msg_merge();
50 static LDAPMessage *ldap_ufn_expand();
51 LDAPFiltDesc *ldap_ufn_setfilter();
52 #endif /* NEEDPROTOS */
53
54 /*
55  * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
56  *                      specify ldapfilter.conf tags for each phase of search
57  *
58  *      ld              LDAP descriptor
59  *      ufncomp         the exploded user friendly name to look for
60  *      ncomp           number of elements in ufncomp
61  *      prefix          where to start searching
62  *      attrs           list of attribute types to return for matches
63  *      attrsonly       1 => attributes only 0 => attributes and values
64  *      res             will contain the result of the search
65  *      cancelproc      routine that returns non-zero if operation should be
66  *                      cancelled.  This can be NULL.  If it is non-NULL, the
67  *                      routine will be called periodically.
68  *      cancelparm      void * that is passed to cancelproc
69  *      tag[123]        the ldapfilter.conf tag that will be used in phases
70  *                      1, 2, and 3 of the search, respectively
71  *
72  * Example:
73  *      char            *attrs[] = { "mail", "title", 0 };
74  *      char            *ufncomp[] = { "howes", "umich", "us", 0 }
75  *      LDAPMessage     *res;
76  *      error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
77  *                      &res, acancelproc, along, "ufn first",
78  *                      "ufn intermediate", "ufn last" );
79  */
80
81 static int
82 ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, char *prefix,
83         char **attrs, int attrsonly, LDAPMessage **res, cancelptype cancelproc,
84         void *cancelparm, char *tag1, char *tag2, char *tag3 )
85 {
86         char            *dn, *ftag;
87         char            **dns;
88         int             max, i, err, scope, phase, tries;
89         LDAPFiltInfo    *fi;
90         LDAPMessage     *tmpcand;
91         LDAPMessage     *candidates;
92         LDAPMessage     *ldap_msg_merge(), *ldap_ufn_expand();
93         static char     *objattrs[] = { "objectClass", NULL };
94
95         /* 
96          * look up ufn components from most to least significant.
97          * there are 3 phases.  
98          *      phase 1 search the root for orgs or countries
99          *      phase 2 search for orgs
100          *      phase 3 search for a person
101          * in phases 1 and 2, we are building a list of candidate DNs,
102          * below which we will search for the final component of the ufn.
103          * for each component we try the filters listed in the
104          * filterconfig file, first one-level (except the last compoment),
105          * then subtree.  if any of them produce any results, we go on to
106          * the next component.
107          */
108
109         *res = NULL;
110         candidates = NULL;
111         phase = 1;
112         for ( ncomp--; ncomp != -1; ncomp-- ) {
113                 if ( *ufncomp[ncomp] == '"' ) {
114                         char    *quote;
115
116                         if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
117                                 *quote = '\0';
118                         strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
119                 }
120                 if ( ncomp == 0 )
121                         phase = 3;
122
123                 switch ( phase ) {
124                 case 1:
125                         ftag = tag1;
126                         scope = LDAP_SCOPE_ONELEVEL;
127                         break;
128                 case 2:
129                         ftag = tag2;
130                         scope = LDAP_SCOPE_ONELEVEL;
131                         break;
132                 case 3:
133                         ftag = tag3;
134                         scope = LDAP_SCOPE_SUBTREE;
135                         break;
136                 }
137
138                 /*
139                  * construct an array of DN's to search below from the
140                  * list of candidates.
141                  */
142
143                 if ( candidates == NULL ) {
144                         if ( prefix != NULL ) {
145                                 if ( (dns = (char **) malloc( sizeof(char *)
146                                     * 2 )) == NULL ) {
147                                         return( ld->ld_errno = LDAP_NO_MEMORY );
148                                 }
149                                 dns[0] = strdup( prefix );
150                                 dns[1] = NULL;
151                         } else {
152                                 dns = NULL;
153                         }
154                 } else {
155                         i = 0, max = 0;
156                         for ( tmpcand = candidates; tmpcand != NULL &&
157                             tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
158                             tmpcand = tmpcand->lm_chain )
159                         {
160                                 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
161                                         continue;
162
163                                 if ( dns == NULL ) {
164                                         if ( (dns = (char **) malloc(
165                                             sizeof(char *) * 8 )) == NULL ) {
166                                                 ld->ld_errno = LDAP_NO_MEMORY;
167                                                 return( LDAP_NO_MEMORY );
168                                         }
169                                         max = 8;
170                                 } else if ( i >= max ) {
171                                         if ( (dns = (char **) realloc( dns,
172                                             sizeof(char *) * 2 * max ))
173                                             == NULL )
174                                         {
175                                                 ld->ld_errno = LDAP_NO_MEMORY;
176                                                 return( LDAP_NO_MEMORY );
177                                         }
178                                         max *= 2;
179                                 }
180                                 dns[i++] = dn;
181                                 dns[i] = NULL;
182                         }
183                         ldap_msgfree( candidates );
184                         candidates = NULL;
185                 }
186                 tries = 0;
187         tryagain:
188                 tries++;
189                 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
190                     ufncomp[ncomp] ); fi != NULL;
191                     fi = ldap_getnextfilter( ld->ld_filtd ) )
192                 {
193                         if ( (candidates = ldap_ufn_expand( ld, cancelproc,
194                             cancelparm, dns, fi->lfi_filter, scope,
195                             phase == 3 ? attrs : objattrs,
196                             phase == 3 ? attrsonly : 1, &err )) != NULL )
197                         {
198                                 break;
199                         }
200
201                         if ( err == -1 || err == LDAP_USER_CANCELLED ) {
202                                 if ( dns != NULL ) {
203                                         ldap_value_free( dns );
204                                         dns = NULL;
205                                 }
206                                 return( err );
207                         }
208                 }
209
210                 if ( candidates == NULL ) {
211                         if ( tries < 2 && phase != 3 ) {
212                                 scope = LDAP_SCOPE_SUBTREE;
213                                 goto tryagain;
214                         } else {
215                                 if ( dns != NULL ) {
216                                         ldap_value_free( dns );
217                                         dns = NULL;
218                                 }
219                                 return( err );
220                         }
221                 }
222
223                 /* go on to the next component */
224                 if ( phase == 1 )
225                         phase++;
226                 if ( dns != NULL ) {
227                         ldap_value_free( dns );
228                         dns = NULL;
229                 }
230         }
231         *res = candidates;
232
233         return( err );
234 }
235
236 int
237 ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly,
238         LDAPMessage **res, cancelptype cancelproc, void *cancelparm,
239         char *tag1, char *tag2, char *tag3 )
240 {
241         char    **ufncomp, **prefixcomp;
242         char    *pbuf;
243         int     ncomp, pcomp, i, err;
244
245         /* initialize the getfilter stuff if it's not already */
246         if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
247             == NULL ) {
248                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
249         }
250
251         /* call ldap_explode_dn() to break the ufn into its components */
252         if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL )
253                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
254         for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
255                 ;       /* NULL */
256
257         /* more than two components => try it fully qualified first */
258         if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
259                 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
260                     attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
261
262                 if ( ldap_count_entries( ld, *res ) > 0 ) {
263                         ldap_value_free( ufncomp );
264                         return( err );
265                 } else {
266                         ldap_msgfree( *res );
267                         *res = NULL;
268                 }
269         }
270
271         if ( ld->ld_ufnprefix == NULL ) {
272                 ldap_value_free( ufncomp );
273                 return( err );
274         }
275
276         /* if that failed, or < 2 components, use the prefix */
277         if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
278                 ldap_value_free( ufncomp );
279                 return( ld->ld_errno = LDAP_LOCAL_ERROR );
280         }
281         for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
282                 ;       /* NULL */
283         if ( (pbuf = (char *) malloc( strlen( ld->ld_ufnprefix ) + 1 ))
284             == NULL ) { 
285                 ldap_value_free( ufncomp );
286                 ldap_value_free( prefixcomp );
287                 return( ld->ld_errno = LDAP_NO_MEMORY );
288         }
289
290         for ( i = 0; i < pcomp; i++ ) {
291                 int     j;
292
293                 *pbuf = '\0';
294                 for ( j = i; j < pcomp; j++ ) {
295                         strcat( pbuf, prefixcomp[j] );
296                         if ( j + 1 < pcomp )
297                                 strcat( pbuf, "," );
298                 }
299                 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
300                     attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
301
302                 if ( ldap_count_entries( ld, *res ) > 0 ) {
303                         break;
304                 } else {
305                         ldap_msgfree( *res );
306                         *res = NULL;
307                 }
308         }
309
310         ldap_value_free( ufncomp );
311         ldap_value_free( prefixcomp );
312         free( pbuf );
313
314         return( err );
315 }
316
317 /*
318  * same as ldap_ufn_search_ct, except without the ability to specify
319  * ldapfilter.conf tags.
320  */
321 int
322 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
323         LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
324 {
325         return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
326             cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
327 }
328
329 /*
330  * same as ldap_ufn_search_c without the cancel function
331  */
332 int
333 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
334         LDAPMessage **res )
335 {
336         struct timeval  tv;
337
338         tv.tv_sec = ld->ld_timelimit;
339
340         return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
341                 ld->ld_timelimit ? ldap_ufn_timeout : NULL,
342                 ld->ld_timelimit ? (void *) &tv : NULL,
343                 "ufn first", "ufn intermediate", "ufn last" ) );
344 }
345
346
347 /*
348  * ldap_msg_merge - merge two ldap search result chains.  the more
349  * serious of the two error result codes is kept.
350  */
351
352 static LDAPMessage *
353 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
354 {
355         LDAPMessage     *end, *aprev, *aend, *bprev, *bend;
356
357         if ( a == NULL )
358                 return( b );
359
360         if ( b == NULL )
361                 return( a );
362
363         /* find the ends of the a and b chains */
364         aprev = NULL;
365         for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
366                 aprev = aend;
367         bprev = NULL;
368         for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
369                 bprev = bend;
370
371         /* keep result a */
372         if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
373                 /* remove result b */
374                 ldap_msgfree( bend );
375                 if ( bprev != NULL )
376                         bprev->lm_chain = NULL;
377                 else
378                         b = NULL;
379                 end = aend;
380                 if ( aprev != NULL )
381                         aprev->lm_chain = NULL;
382                 else
383                         a = NULL;
384         /* keep result b */
385         } else {
386                 /* remove result a */
387                 ldap_msgfree( aend );
388                 if ( aprev != NULL )
389                         aprev->lm_chain = NULL;
390                 else
391                         a = NULL;
392                 end = bend;
393                 if ( bprev != NULL )
394                         bprev->lm_chain = NULL;
395                 else
396                         b = NULL;
397         }
398
399         if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
400             (b == NULL && aprev == NULL) )
401                 return( end );
402
403         if ( a == NULL ) {
404                 bprev->lm_chain = end;
405                 return( b );
406         } else if ( b == NULL ) {
407                 aprev->lm_chain = end;
408                 return( a );
409         } else {
410                 bprev->lm_chain = end;
411                 aprev->lm_chain = b;
412                 return( a );
413         }
414 }
415
416 static LDAPMessage *
417 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
418         char **dns, char *filter, int scope, char **attrs, int aonly,
419         int *err )
420 {
421         LDAPMessage     *tmpcand, *tmpres;
422         char            *dn;
423         int             i, msgid;
424         struct timeval  tv;
425
426         /* search for this component below the current candidates */
427         tmpcand = NULL;
428         i = 0;
429         do {
430                 if ( dns != NULL )
431                         dn = dns[i];
432                 else
433                         dn = "";
434
435                 if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
436                     aonly )) == -1 ) {
437                         ldap_msgfree( tmpcand );
438                         *err = ld->ld_errno;
439                         return( NULL );
440                 }
441
442                 tv.tv_sec = 0;
443                 tv.tv_usec = 100000;    /* 1/10 of a second */
444
445                 do {
446                         *err = ldap_result( ld, msgid, 1, &tv, &tmpres );
447                         if ( *err == 0 && cancelproc != NULL &&
448                             (*cancelproc)( cancelparm ) != 0 ) {
449                                 ldap_abandon( ld, msgid );
450                                 *err = LDAP_USER_CANCELLED;
451                                 ld->ld_errno = LDAP_USER_CANCELLED;
452                         }
453                 } while ( *err == 0 );
454
455                 if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
456                     ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
457                         ldap_msgfree( tmpcand );
458                         return( NULL );
459                 }
460                 
461                 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
462
463                 i++;
464         } while ( dns != NULL && dns[i] != NULL );
465
466         if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
467                 return( tmpcand );
468         } else {
469                 ldap_msgfree( tmpcand );
470                 return( NULL );
471         }
472 }
473
474 /*
475  * ldap_ufn_setfilter - set the filter config file used in ufn searching
476  */
477
478 LDAPFiltDesc *
479 ldap_ufn_setfilter( LDAP *ld, char *fname )
480 {
481         if ( ld->ld_filtd != NULL )
482                 ldap_getfilter_free( ld->ld_filtd );
483
484         return( ld->ld_filtd = ldap_init_getfilter( fname ) );
485 }
486
487 void
488 ldap_ufn_setprefix( LDAP *ld, char *prefix )
489 {
490         if ( ld->ld_ufnprefix != NULL )
491                 free( ld->ld_ufnprefix );
492
493         ld->ld_ufnprefix = strdup( prefix );
494 }
495
496 int
497 ldap_ufn_timeout( void *tvparam )
498 {
499         struct timeval  *tv;
500
501         tv = (struct timeval *)tvparam;
502
503         if ( tv->tv_sec != 0 ) {
504                 tv->tv_usec = tv->tv_sec * 1000000;     /* sec => micro sec */
505                 tv->tv_sec = 0;
506         }
507         tv->tv_usec -= 100000;  /* 1/10 of a second */
508
509         return( tv->tv_usec <= 0 ? 1 : 0 );
510 }