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