3 * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
7 * Copyright (c) 1990 Regents of the University of Michigan.
17 #include <ac/stdlib.h>
19 #include <ac/socket.h>
20 #include <ac/string.h>
24 #include "ldap_defaults.h"
26 typedef int (*cancelptype) LDAP_P(( void *cancelparm ));
29 static int ldap_ufn_search_ctx LDAP_P(( LDAP *ld, char **ufncomp, int ncomp,
30 char *prefix, char **attrs, int attrsonly, LDAPMessage **res,
31 cancelptype cancelproc, void *cancelparm, char *tag1, char *tag2,
33 static LDAPMessage *ldap_msg_merge LDAP_P(( LDAP *ld, LDAPMessage *a, LDAPMessage *b ));
34 static LDAPMessage *ldap_ufn_expand LDAP_P(( LDAP *ld, cancelptype cancelproc,
35 void *cancelparm, char **dns, char *filter, int scope,
36 char **attrs, int aonly, int *err ));
39 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
40 * specify ldapfilter.conf tags for each phase of search
43 * ufncomp the exploded user friendly name to look for
44 * ncomp number of elements in ufncomp
45 * prefix where to start searching
46 * attrs list of attribute types to return for matches
47 * attrsonly 1 => attributes only 0 => attributes and values
48 * res will contain the result of the search
49 * cancelproc routine that returns non-zero if operation should be
50 * cancelled. This can be a null function pointer. If
51 * it is not 0, the routine will be called periodically.
52 * cancelparm void * that is passed to cancelproc
53 * tag[123] the ldapfilter.conf tag that will be used in phases
54 * 1, 2, and 3 of the search, respectively
57 * char *attrs[] = { "mail", "title", 0 };
58 * char *ufncomp[] = { "howes", "umich", "us", 0 }
60 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
61 * &res, acancelproc, along, "ufn first",
62 * "ufn intermediate", "ufn last" );
66 ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, char *prefix,
67 char **attrs, int attrsonly, LDAPMessage **res, cancelptype cancelproc,
68 void *cancelparm, char *tag1, char *tag2, char *tag3 )
70 char *dn, *ftag = NULL;
72 int max, i, err, scope = 0, phase, tries;
75 LDAPMessage *candidates;
76 static char *objattrs[] = { "objectClass", NULL };
79 * look up ufn components from most to least significant.
81 * phase 1 search the root for orgs or countries
82 * phase 2 search for orgs
83 * phase 3 search for a person
84 * in phases 1 and 2, we are building a list of candidate DNs,
85 * below which we will search for the final component of the ufn.
86 * for each component we try the filters listed in the
87 * filterconfig file, first one-level (except the last compoment),
88 * then subtree. if any of them produce any results, we go on to
95 for ( ncomp--; ncomp != -1; ncomp-- ) {
96 if ( *ufncomp[ncomp] == '"' ) {
99 if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
101 SAFEMEMCPY( ufncomp[ncomp], ufncomp[ncomp] + 1,
102 strlen( ufncomp[ncomp] + 1 ) + 1 );
110 scope = LDAP_SCOPE_ONELEVEL;
114 scope = LDAP_SCOPE_ONELEVEL;
118 scope = LDAP_SCOPE_SUBTREE;
123 * construct an array of DN's to search below from the
124 * list of candidates.
127 if ( candidates == NULL ) {
128 if ( prefix != NULL ) {
129 if ( (dns = (char **) LDAP_MALLOC( sizeof(char *)
131 return( ld->ld_errno = LDAP_NO_MEMORY );
133 dns[0] = LDAP_STRDUP( prefix );
140 for ( tmpcand = candidates; tmpcand != NULL &&
141 tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
142 tmpcand = tmpcand->lm_chain )
144 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
148 if ( (dns = (char **) LDAP_MALLOC(
149 sizeof(char *) * 8 )) == NULL ) {
150 ld->ld_errno = LDAP_NO_MEMORY;
151 return( LDAP_NO_MEMORY );
154 } else if ( i >= max ) {
155 if ( (dns = (char **) LDAP_REALLOC( dns,
156 sizeof(char *) * 2 * max ))
159 ld->ld_errno = LDAP_NO_MEMORY;
160 return( LDAP_NO_MEMORY );
167 ldap_msgfree( candidates );
173 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
174 ufncomp[ncomp] ); fi != NULL;
175 fi = ldap_getnextfilter( ld->ld_filtd ) )
177 if ( (candidates = ldap_ufn_expand( ld, cancelproc,
178 cancelparm, dns, fi->lfi_filter, scope,
179 phase == 3 ? attrs : objattrs,
180 phase == 3 ? attrsonly : 1, &err )) != NULL )
185 if ( err == -1 || err == LDAP_USER_CANCELLED ) {
194 if ( candidates == NULL ) {
195 if ( tries < 2 && phase != 3 ) {
196 scope = LDAP_SCOPE_SUBTREE;
207 /* go on to the next component */
222 LDAP *ld, LDAP_CONST char *ufn, char **attrs, int attrsonly,
223 LDAPMessage **res, cancelptype cancelproc, void *cancelparm,
224 char *tag1, char *tag2, char *tag3 )
226 char **ufncomp, **prefixcomp;
228 int ncomp, pcomp, i, err = 0;
230 /* initialize the getfilter stuff if it's not already */
231 if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
233 return( ld->ld_errno = LDAP_LOCAL_ERROR );
236 /* call ldap_explode_dn() to break the ufn into its components */
237 if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL )
238 return( ld->ld_errno = LDAP_LOCAL_ERROR );
239 for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
242 /* more than two components => try it fully qualified first */
243 if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
244 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
245 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
247 if ( ldap_count_entries( ld, *res ) > 0 ) {
248 LDAP_VFREE( ufncomp );
251 ldap_msgfree( *res );
256 if ( ld->ld_ufnprefix == NULL ) {
257 LDAP_VFREE( ufncomp );
261 /* if that failed, or < 2 components, use the prefix */
262 if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
263 LDAP_VFREE( ufncomp );
264 return( ld->ld_errno = LDAP_LOCAL_ERROR );
266 for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
268 if ( (pbuf = (char *) LDAP_MALLOC( strlen( ld->ld_ufnprefix ) + 1 ))
270 LDAP_VFREE( ufncomp );
271 LDAP_VFREE( prefixcomp );
272 return( ld->ld_errno = LDAP_NO_MEMORY );
275 for ( i = 0; i < pcomp; i++ ) {
279 for ( j = i; j < pcomp; j++ ) {
280 strcat( pbuf, prefixcomp[j] );
284 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
285 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
287 if ( ldap_count_entries( ld, *res ) > 0 ) {
290 ldap_msgfree( *res );
295 LDAP_VFREE( ufncomp );
296 LDAP_VFREE( prefixcomp );
303 * same as ldap_ufn_search_ct, except without the ability to specify
304 * ldapfilter.conf tags.
308 LDAP *ld, LDAP_CONST char *ufn, char **attrs, int attrsonly,
309 LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
311 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
312 cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
316 * same as ldap_ufn_search_c without the cancel function
320 LDAP *ld, LDAP_CONST char *ufn, char **attrs, int attrsonly,
325 tv.tv_sec = ld->ld_timelimit;
327 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
328 ld->ld_timelimit ? ldap_ufn_timeout : NULL,
329 ld->ld_timelimit ? (void *) &tv : NULL,
330 "ufn first", "ufn intermediate", "ufn last" ) );
335 * ldap_msg_merge - merge two ldap search result chains. the more
336 * serious of the two error result codes is kept.
340 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
342 LDAPMessage *end, *aprev, *aend, *bprev, *bend;
350 /* find the ends of the a and b chains */
352 for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
355 for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
359 if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
360 /* remove result b */
361 ldap_msgfree( bend );
363 bprev->lm_chain = NULL;
368 aprev->lm_chain = NULL;
373 /* remove result a */
374 ldap_msgfree( aend );
376 aprev->lm_chain = NULL;
381 bprev->lm_chain = NULL;
386 if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
387 (b == NULL && aprev == NULL) )
391 bprev->lm_chain = end;
393 } else if ( b == NULL ) {
394 aprev->lm_chain = end;
397 bprev->lm_chain = end;
404 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
405 char **dns, char *filter, int scope, char **attrs, int aonly,
408 LDAPMessage *tmpcand, *tmpres;
413 /* search for this component below the current candidates */
422 if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
424 ldap_msgfree( tmpcand );
430 tv.tv_usec = 100000; /* 1/10 of a second */
433 *err = ldap_result( ld, msgid, 1, &tv, &tmpres );
434 if ( *err == 0 && cancelproc != 0 &&
435 (*cancelproc)( cancelparm ) != 0 ) {
436 ldap_abandon( ld, msgid );
437 *err = LDAP_USER_CANCELLED;
438 ld->ld_errno = LDAP_USER_CANCELLED;
440 } while ( *err == 0 );
442 if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
443 ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
444 ldap_msgfree( tmpcand );
448 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
451 } while ( dns != NULL && dns[i] != NULL );
453 if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
456 ldap_msgfree( tmpcand );
462 * ldap_ufn_setfilter - set the filter config file used in ufn searching
466 ldap_ufn_setfilter( LDAP *ld, LDAP_CONST char *fname )
468 if ( ld->ld_filtd != NULL )
469 ldap_getfilter_free( ld->ld_filtd );
471 return( ld->ld_filtd = ldap_init_getfilter( fname ) );
475 ldap_ufn_setprefix( LDAP *ld, LDAP_CONST char *prefix )
477 if ( ld->ld_ufnprefix != NULL )
478 LDAP_FREE( ld->ld_ufnprefix );
480 ld->ld_ufnprefix = prefix == NULL
481 ? NULL : LDAP_STRDUP( prefix );
485 ldap_ufn_timeout( void *tvparam )
489 tv = (struct timeval *)tvparam;
491 if ( tv->tv_sec != 0 ) {
492 tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */
495 tv->tv_usec -= 100000; /* 1/10 of a second */
497 return( tv->tv_usec <= 0 ? 1 : 0 );