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>
33 typedef int (*cancelptype)( void *cancelparm );
34 #else /* NEEDPROTOS */
35 typedef int (*cancelptype)();
36 #endif /* 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,
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 */
55 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
56 * specify ldapfilter.conf tags for each phase of search
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
73 * char *attrs[] = { "mail", "title", 0 };
74 * char *ufncomp[] = { "howes", "umich", "us", 0 }
76 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
77 * &res, acancelproc, along, "ufn first",
78 * "ufn intermediate", "ufn last" );
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 )
88 int max, i, err, scope, phase, tries;
91 LDAPMessage *candidates;
92 LDAPMessage *ldap_msg_merge(), *ldap_ufn_expand();
93 static char *objattrs[] = { "objectClass", NULL };
96 * look up ufn components from most to least significant.
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.
112 for ( ncomp--; ncomp != -1; ncomp-- ) {
113 if ( *ufncomp[ncomp] == '"' ) {
116 if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
118 strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
126 scope = LDAP_SCOPE_ONELEVEL;
130 scope = LDAP_SCOPE_ONELEVEL;
134 scope = LDAP_SCOPE_SUBTREE;
139 * construct an array of DN's to search below from the
140 * list of candidates.
143 if ( candidates == NULL ) {
144 if ( prefix != NULL ) {
145 if ( (dns = (char **) malloc( sizeof(char *)
147 return( ld->ld_errno = LDAP_NO_MEMORY );
149 dns[0] = strdup( prefix );
156 for ( tmpcand = candidates; tmpcand != NULL &&
157 tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
158 tmpcand = tmpcand->lm_chain )
160 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
164 if ( (dns = (char **) malloc(
165 sizeof(char *) * 8 )) == NULL ) {
166 ld->ld_errno = LDAP_NO_MEMORY;
167 return( LDAP_NO_MEMORY );
170 } else if ( i >= max ) {
171 if ( (dns = (char **) realloc( dns,
172 sizeof(char *) * 2 * max ))
175 ld->ld_errno = LDAP_NO_MEMORY;
176 return( LDAP_NO_MEMORY );
183 ldap_msgfree( candidates );
189 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
190 ufncomp[ncomp] ); fi != NULL;
191 fi = ldap_getnextfilter( ld->ld_filtd ) )
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 )
201 if ( err == -1 || err == LDAP_USER_CANCELLED ) {
203 ldap_value_free( dns );
210 if ( candidates == NULL ) {
211 if ( tries < 2 && phase != 3 ) {
212 scope = LDAP_SCOPE_SUBTREE;
216 ldap_value_free( dns );
223 /* go on to the next component */
227 ldap_value_free( dns );
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 )
241 char **ufncomp, **prefixcomp;
243 int ncomp, pcomp, i, err;
245 /* initialize the getfilter stuff if it's not already */
246 if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
248 return( ld->ld_errno = LDAP_LOCAL_ERROR );
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++ )
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 );
262 if ( ldap_count_entries( ld, *res ) > 0 ) {
263 ldap_value_free( ufncomp );
266 ldap_msgfree( *res );
271 if ( ld->ld_ufnprefix == NULL ) {
272 ldap_value_free( ufncomp );
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 );
281 for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
283 if ( (pbuf = (char *) malloc( strlen( ld->ld_ufnprefix ) + 1 ))
285 ldap_value_free( ufncomp );
286 ldap_value_free( prefixcomp );
287 return( ld->ld_errno = LDAP_NO_MEMORY );
290 for ( i = 0; i < pcomp; i++ ) {
294 for ( j = i; j < pcomp; j++ ) {
295 strcat( pbuf, prefixcomp[j] );
299 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
300 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
302 if ( ldap_count_entries( ld, *res ) > 0 ) {
305 ldap_msgfree( *res );
310 ldap_value_free( ufncomp );
311 ldap_value_free( prefixcomp );
318 * same as ldap_ufn_search_ct, except without the ability to specify
319 * ldapfilter.conf tags.
322 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
323 LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
325 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
326 cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
330 * same as ldap_ufn_search_c without the cancel function
333 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
338 tv.tv_sec = ld->ld_timelimit;
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" ) );
348 * ldap_msg_merge - merge two ldap search result chains. the more
349 * serious of the two error result codes is kept.
353 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
355 LDAPMessage *end, *aprev, *aend, *bprev, *bend;
363 /* find the ends of the a and b chains */
365 for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
368 for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
372 if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
373 /* remove result b */
374 ldap_msgfree( bend );
376 bprev->lm_chain = NULL;
381 aprev->lm_chain = NULL;
386 /* remove result a */
387 ldap_msgfree( aend );
389 aprev->lm_chain = NULL;
394 bprev->lm_chain = NULL;
399 if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
400 (b == NULL && aprev == NULL) )
404 bprev->lm_chain = end;
406 } else if ( b == NULL ) {
407 aprev->lm_chain = end;
410 bprev->lm_chain = end;
417 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
418 char **dns, char *filter, int scope, char **attrs, int aonly,
421 LDAPMessage *tmpcand, *tmpres;
426 /* search for this component below the current candidates */
435 if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
437 ldap_msgfree( tmpcand );
443 tv.tv_usec = 100000; /* 1/10 of a second */
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;
453 } while ( *err == 0 );
455 if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
456 ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
457 ldap_msgfree( tmpcand );
461 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
464 } while ( dns != NULL && dns[i] != NULL );
466 if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
469 ldap_msgfree( tmpcand );
475 * ldap_ufn_setfilter - set the filter config file used in ufn searching
479 ldap_ufn_setfilter( LDAP *ld, char *fname )
481 if ( ld->ld_filtd != NULL )
482 ldap_getfilter_free( ld->ld_filtd );
484 return( ld->ld_filtd = ldap_init_getfilter( fname ) );
488 ldap_ufn_setprefix( LDAP *ld, char *prefix )
490 if ( ld->ld_ufnprefix != NULL )
491 free( ld->ld_ufnprefix );
493 ld->ld_ufnprefix = strdup( prefix );
497 ldap_ufn_timeout( void *tvparam )
501 tv = (struct timeval *)tvparam;
503 if ( tv->tv_sec != 0 ) {
504 tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */
507 tv->tv_usec -= 100000; /* 1/10 of a second */
509 return( tv->tv_usec <= 0 ? 1 : 0 );