2 * Copyright (c) 1990 Regents of the University of Michigan.
9 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
20 #if defined( DOS ) || defined( _WIN32 )
24 #include <sys/types.h>
25 #include <sys/socket.h>
32 #include "ldapconfig.h"
35 typedef int (*cancelptype)( void *cancelparm );
36 #else /* NEEDPROTOS */
37 typedef int (*cancelptype)();
38 #endif /* NEEDPROTOS */
41 static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp,
42 char *prefix, char **attrs, int attrsonly, LDAPMessage **res,
43 cancelptype cancelproc, void *cancelparm, char *tag1, char *tag2,
45 static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
46 static LDAPMessage *ldap_ufn_expand( LDAP *ld, cancelptype cancelproc,
47 void *cancelparm, char **dns, char *filter, int scope,
48 char **attrs, int aonly, int *err );
49 LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
50 #else /* NEEDPROTOS */
51 static LDAPMessage *ldap_msg_merge();
52 static LDAPMessage *ldap_ufn_expand();
53 LDAPFiltDesc *ldap_ufn_setfilter();
54 #endif /* NEEDPROTOS */
57 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
58 * specify ldapfilter.conf tags for each phase of search
61 * ufncomp the exploded user friendly name to look for
62 * ncomp number of elements in ufncomp
63 * prefix where to start searching
64 * attrs list of attribute types to return for matches
65 * attrsonly 1 => attributes only 0 => attributes and values
66 * res will contain the result of the search
67 * cancelproc routine that returns non-zero if operation should be
68 * cancelled. This can be NULL. If it is non-NULL, the
69 * routine will be called periodically.
70 * cancelparm void * that is passed to cancelproc
71 * tag[123] the ldapfilter.conf tag that will be used in phases
72 * 1, 2, and 3 of the search, respectively
75 * char *attrs[] = { "mail", "title", 0 };
76 * char *ufncomp[] = { "howes", "umich", "us", 0 }
78 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
79 * &res, acancelproc, along, "ufn first",
80 * "ufn intermediate", "ufn last" );
84 ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, char *prefix,
85 char **attrs, int attrsonly, LDAPMessage **res, cancelptype cancelproc,
86 void *cancelparm, char *tag1, char *tag2, char *tag3 )
90 int max, i, err, scope, phase, tries;
93 LDAPMessage *candidates;
94 LDAPMessage *ldap_msg_merge(), *ldap_ufn_expand();
95 static char *objattrs[] = { "objectClass", NULL };
98 * look up ufn components from most to least significant.
100 * phase 1 search the root for orgs or countries
101 * phase 2 search for orgs
102 * phase 3 search for a person
103 * in phases 1 and 2, we are building a list of candidate DNs,
104 * below which we will search for the final component of the ufn.
105 * for each component we try the filters listed in the
106 * filterconfig file, first one-level (except the last compoment),
107 * then subtree. if any of them produce any results, we go on to
108 * the next component.
114 for ( ncomp--; ncomp != -1; ncomp-- ) {
115 if ( *ufncomp[ncomp] == '"' ) {
118 if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
120 strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
128 scope = LDAP_SCOPE_ONELEVEL;
132 scope = LDAP_SCOPE_ONELEVEL;
136 scope = LDAP_SCOPE_SUBTREE;
141 * construct an array of DN's to search below from the
142 * list of candidates.
145 if ( candidates == NULL ) {
146 if ( prefix != NULL ) {
147 if ( (dns = (char **) malloc( sizeof(char *)
149 return( ld->ld_errno = LDAP_NO_MEMORY );
151 dns[0] = strdup( prefix );
158 for ( tmpcand = candidates; tmpcand != NULL &&
159 tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
160 tmpcand = tmpcand->lm_chain )
162 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
166 if ( (dns = (char **) malloc(
167 sizeof(char *) * 8 )) == NULL ) {
168 ld->ld_errno = LDAP_NO_MEMORY;
169 return( LDAP_NO_MEMORY );
172 } else if ( i >= max ) {
173 if ( (dns = (char **) realloc( dns,
174 sizeof(char *) * 2 * max ))
177 ld->ld_errno = LDAP_NO_MEMORY;
178 return( LDAP_NO_MEMORY );
185 ldap_msgfree( candidates );
191 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
192 ufncomp[ncomp] ); fi != NULL;
193 fi = ldap_getnextfilter( ld->ld_filtd ) )
195 if ( (candidates = ldap_ufn_expand( ld, cancelproc,
196 cancelparm, dns, fi->lfi_filter, scope,
197 phase == 3 ? attrs : objattrs,
198 phase == 3 ? attrsonly : 1, &err )) != NULL )
203 if ( err == -1 || err == LDAP_USER_CANCELLED ) {
205 ldap_value_free( dns );
212 if ( candidates == NULL ) {
213 if ( tries < 2 && phase != 3 ) {
214 scope = LDAP_SCOPE_SUBTREE;
218 ldap_value_free( dns );
225 /* go on to the next component */
229 ldap_value_free( dns );
239 ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly,
240 LDAPMessage **res, cancelptype cancelproc, void *cancelparm,
241 char *tag1, char *tag2, char *tag3 )
243 char **ufncomp, **prefixcomp;
245 int ncomp, pcomp, i, err;
247 /* initialize the getfilter stuff if it's not already */
248 if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
250 return( ld->ld_errno = LDAP_LOCAL_ERROR );
253 /* call ldap_explode_dn() to break the ufn into its components */
254 if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL )
255 return( ld->ld_errno = LDAP_LOCAL_ERROR );
256 for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
259 /* more than two components => try it fully qualified first */
260 if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
261 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
262 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
264 if ( ldap_count_entries( ld, *res ) > 0 ) {
265 ldap_value_free( ufncomp );
268 ldap_msgfree( *res );
273 if ( ld->ld_ufnprefix == NULL ) {
274 ldap_value_free( ufncomp );
278 /* if that failed, or < 2 components, use the prefix */
279 if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
280 ldap_value_free( ufncomp );
281 return( ld->ld_errno = LDAP_LOCAL_ERROR );
283 for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
285 if ( (pbuf = (char *) malloc( strlen( ld->ld_ufnprefix ) + 1 ))
287 ldap_value_free( ufncomp );
288 ldap_value_free( prefixcomp );
289 return( ld->ld_errno = LDAP_NO_MEMORY );
292 for ( i = 0; i < pcomp; i++ ) {
296 for ( j = i; j < pcomp; j++ ) {
297 strcat( pbuf, prefixcomp[j] );
301 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
302 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
304 if ( ldap_count_entries( ld, *res ) > 0 ) {
307 ldap_msgfree( *res );
312 ldap_value_free( ufncomp );
313 ldap_value_free( prefixcomp );
320 * same as ldap_ufn_search_ct, except without the ability to specify
321 * ldapfilter.conf tags.
324 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
325 LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
327 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
328 cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
332 * same as ldap_ufn_search_c without the cancel function
335 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
340 tv.tv_sec = ld->ld_timelimit;
342 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
343 ld->ld_timelimit ? ldap_ufn_timeout : NULL,
344 ld->ld_timelimit ? (void *) &tv : NULL,
345 "ufn first", "ufn intermediate", "ufn last" ) );
350 * ldap_msg_merge - merge two ldap search result chains. the more
351 * serious of the two error result codes is kept.
355 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
357 LDAPMessage *end, *aprev, *aend, *bprev, *bend;
365 /* find the ends of the a and b chains */
367 for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
370 for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
374 if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
375 /* remove result b */
376 ldap_msgfree( bend );
378 bprev->lm_chain = NULL;
383 aprev->lm_chain = NULL;
388 /* remove result a */
389 ldap_msgfree( aend );
391 aprev->lm_chain = NULL;
396 bprev->lm_chain = NULL;
401 if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
402 (b == NULL && aprev == NULL) )
406 bprev->lm_chain = end;
408 } else if ( b == NULL ) {
409 aprev->lm_chain = end;
412 bprev->lm_chain = end;
419 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
420 char **dns, char *filter, int scope, char **attrs, int aonly,
423 LDAPMessage *tmpcand, *tmpres;
428 /* search for this component below the current candidates */
437 if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
439 ldap_msgfree( tmpcand );
445 tv.tv_usec = 100000; /* 1/10 of a second */
448 *err = ldap_result( ld, msgid, 1, &tv, &tmpres );
449 if ( *err == 0 && cancelproc != NULL &&
450 (*cancelproc)( cancelparm ) != 0 ) {
451 ldap_abandon( ld, msgid );
452 *err = LDAP_USER_CANCELLED;
453 ld->ld_errno = LDAP_USER_CANCELLED;
455 } while ( *err == 0 );
457 if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
458 ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
459 ldap_msgfree( tmpcand );
463 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
466 } while ( dns != NULL && dns[i] != NULL );
468 if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
471 ldap_msgfree( tmpcand );
477 * ldap_ufn_setfilter - set the filter config file used in ufn searching
481 ldap_ufn_setfilter( LDAP *ld, char *fname )
483 if ( ld->ld_filtd != NULL )
484 ldap_getfilter_free( ld->ld_filtd );
486 return( ld->ld_filtd = ldap_init_getfilter( fname ) );
490 ldap_ufn_setprefix( LDAP *ld, char *prefix )
492 if ( ld->ld_ufnprefix != NULL )
493 free( ld->ld_ufnprefix );
495 ld->ld_ufnprefix = strdup( prefix );
499 ldap_ufn_timeout( void *tvparam )
503 tv = (struct timeval *)tvparam;
505 if ( tv->tv_sec != 0 ) {
506 tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */
509 tv->tv_usec -= 100000; /* 1/10 of a second */
511 return( tv->tv_usec <= 0 ? 1 : 0 );