diff options
Diffstat (limited to 'ldap/c-sdk/libldap/ufn.c')
-rw-r--r-- | ldap/c-sdk/libldap/ufn.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/ldap/c-sdk/libldap/ufn.c b/ldap/c-sdk/libldap/ufn.c new file mode 100644 index 000000000..54e737e15 --- /dev/null +++ b/ldap/c-sdk/libldap/ufn.c @@ -0,0 +1,563 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * ufn.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +typedef int (LDAP_CALL *cancelptype)( void *cancelparm ); + +static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, + char *prefix, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, + char *tag1, char *tag2, char *tag3 ); +static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b ); +static LDAPMessage *ldap_ufn_expand( LDAP *ld, + LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, char **dns, + char *filter, int scope, char **attrs, int aonly, int *err ); + +/* + * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature; + * specify ldapfilter.conf tags for each phase of search + * + * ld LDAP descriptor + * ufncomp the exploded user friendly name to look for + * ncomp number of elements in ufncomp + * prefix where to start searching + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * res will contain the result of the search + * cancelproc routine that returns non-zero if operation should be + * cancelled. This can be NULL. If it is non-NULL, the + * routine will be called periodically. + * cancelparm void * that is passed to cancelproc + * tag[123] the ldapfilter.conf tag that will be used in phases + * 1, 2, and 3 of the search, respectively + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * char *ufncomp[] = { "howes", "umich", "us", 0 } + * LDAPMessage *res; + * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly, + * &res, acancelproc, along, "ufn first", + * "ufn intermediate", "ufn last" ); + */ + +static int +ldap_ufn_search_ctx( + LDAP *ld, + char **ufncomp, + int ncomp, + char *prefix, + char **attrs, + int attrsonly, + LDAPMessage **res, + LDAP_CANCELPROC_CALLBACK *cancelproc, + void *cancelparm, + char *tag1, + char *tag2, + char *tag3 +) +{ + char *dn, *ftag = NULL; + char **dns = NULL; + int max, i, err, scope = 0, phase, tries; + LDAPFiltInfo *fi; + LDAPMessage *tmpcand; + LDAPMessage *candidates; + static char *objattrs[] = { "objectClass", NULL }; + + /* + * look up ufn components from most to least significant. + * there are 3 phases. + * phase 1 search the root for orgs or countries + * phase 2 search for orgs + * phase 3 search for a person + * in phases 1 and 2, we are building a list of candidate DNs, + * below which we will search for the final component of the ufn. + * for each component we try the filters listed in the + * filterconfig file, first one-level (except the last compoment), + * then subtree. if any of them produce any results, we go on to + * the next component. + */ + + *res = NULL; + candidates = NULL; + phase = 1; + for ( ncomp--; ncomp != -1; ncomp-- ) { + if ( *ufncomp[ncomp] == '"' ) { + char *quote; + + if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL ) + *quote = '\0'; + strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 ); + } + if ( ncomp == 0 ) + phase = 3; + + switch ( phase ) { + case 1: + ftag = tag1; + scope = LDAP_SCOPE_ONELEVEL; + break; + case 2: + ftag = tag2; + scope = LDAP_SCOPE_ONELEVEL; + break; + case 3: + ftag = tag3; + scope = LDAP_SCOPE_SUBTREE; + break; + } + + /* + * construct an array of DN's to search below from the + * list of candidates. + */ + + if ( candidates == NULL ) { + if ( prefix != NULL ) { + if ( (dns = (char **)NSLDAPI_MALLOC( + sizeof(char *) * 2 )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + dns[0] = nsldapi_strdup( prefix ); + dns[1] = NULL; + } else { + dns = NULL; + } + } else { + i = 0, max = 0; + for ( tmpcand = candidates; tmpcand != NULL && + tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT; + tmpcand = tmpcand->lm_chain ) + { + if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL ) + continue; + + if ( dns == NULL ) { + if ( (dns = (char **)NSLDAPI_MALLOC( + sizeof(char *) * 8 )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, + NULL, NULL ); + return( err ); + } + max = 8; + } else if ( i >= max ) { + if ( (dns = (char **)NSLDAPI_REALLOC( + dns, sizeof(char *) * 2 * max )) + == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, + NULL, NULL ); + return( err ); + } + max *= 2; + } + dns[i++] = dn; + dns[i] = NULL; + } + ldap_msgfree( candidates ); + candidates = NULL; + } + tries = 0; + tryagain: + tries++; + for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag, + ufncomp[ncomp] ); fi != NULL; + fi = ldap_getnextfilter( ld->ld_filtd ) ) + { + if ( (candidates = ldap_ufn_expand( ld, cancelproc, + cancelparm, dns, fi->lfi_filter, scope, + phase == 3 ? attrs : objattrs, + phase == 3 ? attrsonly : 1, &err )) != NULL ) + { + break; + } + + if ( err == -1 || err == LDAP_USER_CANCELLED ) { + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + return( err ); + } + } + + if ( candidates == NULL ) { + if ( tries < 2 && phase != 3 ) { + scope = LDAP_SCOPE_SUBTREE; + goto tryagain; + } else { + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + return( err ); + } + } + + /* go on to the next component */ + if ( phase == 1 ) + phase++; + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + } + *res = candidates; + + return( err ); +} + +int +LDAP_CALL +ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, + char *tag1, char *tag2, char *tag3 ) +{ + char **ufncomp, **prefixcomp; + char *pbuf; + int ncomp, pcomp, i, err = 0; + + /* getfilter stuff must be inited before we are called */ + if ( ld->ld_filtd == NULL ) { + err = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + /* call ldap_explode_dn() to break the ufn into its components */ + if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL ) { + err = LDAP_LOCAL_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ ) + ; /* NULL */ + + /* more than two components => try it fully qualified first */ + if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) { + err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs, + attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 ); + + if ( ldap_count_entries( ld, *res ) > 0 ) { + ldap_value_free( ufncomp ); + return( err ); + } else { + ldap_msgfree( *res ); + *res = NULL; + } + } + + if ( ld->ld_ufnprefix == NULL ) { + ldap_value_free( ufncomp ); + return( err ); + } + + /* if that failed, or < 2 components, use the prefix */ + if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) { + ldap_value_free( ufncomp ); + err = LDAP_LOCAL_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ ) + ; /* NULL */ + if ( (pbuf = (char *)NSLDAPI_MALLOC( strlen( ld->ld_ufnprefix ) + 1 )) + == NULL ) { + ldap_value_free( ufncomp ); + ldap_value_free( prefixcomp ); + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + for ( i = 0; i < pcomp; i++ ) { + int j; + + *pbuf = '\0'; + for ( j = i; j < pcomp; j++ ) { + strcat( pbuf, prefixcomp[j] ); + if ( j + 1 < pcomp ) + strcat( pbuf, "," ); + } + err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs, + attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 ); + + if ( ldap_count_entries( ld, *res ) > 0 ) { + break; + } else { + ldap_msgfree( *res ); + *res = NULL; + } + } + + ldap_value_free( ufncomp ); + ldap_value_free( prefixcomp ); + NSLDAPI_FREE( pbuf ); + + return( err ); +} + +/* + * same as ldap_ufn_search_ct, except without the ability to specify + * ldapfilter.conf tags. + */ +int +LDAP_CALL +ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm ) +{ + return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc, + cancelparm, "ufn first", "ufn intermediate", "ufn last" ) ); +} + +/* + * same as ldap_ufn_search_c without the cancel function + */ +int +LDAP_CALL +ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res ) +{ + struct timeval tv; + + tv.tv_sec = ld->ld_timelimit; + + return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, + ld->ld_timelimit ? ldap_ufn_timeout : NULL, + ld->ld_timelimit ? (void *) &tv : NULL, + "ufn first", "ufn intermediate", "ufn last" ) ); +} + + +/* + * ldap_msg_merge - merge two ldap search result chains. the more + * serious of the two error result codes is kept. + */ + +static LDAPMessage * +ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b ) +{ + LDAPMessage *end, *aprev, *aend, *bprev, *bend; + + if ( a == NULL ) + return( b ); + + if ( b == NULL ) + return( a ); + + /* find the ends of the a and b chains */ + aprev = NULL; + for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain ) + aprev = aend; + bprev = NULL; + for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain ) + bprev = bend; + + /* keep result a */ + if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) { + /* remove result b */ + ldap_msgfree( bend ); + if ( bprev != NULL ) + bprev->lm_chain = NULL; + else + b = NULL; + end = aend; + if ( aprev != NULL ) + aprev->lm_chain = NULL; + else + a = NULL; + /* keep result b */ + } else { + /* remove result a */ + ldap_msgfree( aend ); + if ( aprev != NULL ) + aprev->lm_chain = NULL; + else + a = NULL; + end = bend; + if ( bprev != NULL ) + bprev->lm_chain = NULL; + else + b = NULL; + } + + if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) || + (b == NULL && aprev == NULL) ) + return( end ); + + if ( a == NULL ) { + bprev->lm_chain = end; + return( b ); + } else if ( b == NULL ) { + aprev->lm_chain = end; + return( a ); + } else { + bprev->lm_chain = end; + aprev->lm_chain = b; + return( a ); + } +} + +static LDAPMessage * +ldap_ufn_expand( LDAP *ld, LDAP_CANCELPROC_CALLBACK *cancelproc, + void *cancelparm, char **dns, char *filter, int scope, + char **attrs, int aonly, int *err ) +{ + LDAPMessage *tmpcand, *tmpres; + char *dn; + int i, msgid; + struct timeval tv; + + /* search for this component below the current candidates */ + tmpcand = NULL; + i = 0; + do { + if ( dns != NULL ) + dn = dns[i]; + else + dn = ""; + + if (( msgid = ldap_search( ld, dn, scope, filter, attrs, + aonly )) == -1 ) { + ldap_msgfree( tmpcand ); + tmpcand = NULL; + *err = LDAP_GET_LDERRNO( ld, NULL, NULL ); + /* + * Compiling with gcc-4.2 on Mac: + * gcc-4.2 -arch ppc -c -o ufn.o -gdwarf-2 -01 ufn.c + * having a return NULL statement here causes gcc to + * hang. Therefore set tmpcand to null (above) and break + * out of this loop to make gcc happy. + */ + break; + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; /* 1/10 of a second */ + + do { + *err = ldap_result( ld, msgid, 1, &tv, &tmpres ); + if ( *err == 0 && cancelproc != NULL && + (*cancelproc)( cancelparm ) != 0 ) { + ldap_abandon( ld, msgid ); + *err = LDAP_USER_CANCELLED; + LDAP_SET_LDERRNO( ld, *err, NULL, NULL ); + } + } while ( *err == 0 ); + + if ( *err == LDAP_USER_CANCELLED || *err < 0 || + ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) { + ldap_msgfree( tmpcand ); + return( NULL ); + } + + tmpcand = ldap_msg_merge( ld, tmpcand, tmpres ); + + i++; + } while ( dns != NULL && dns[i] != NULL ); + + /* Catch the tmpcand = NULL case as required by breaking out the loop + * to prevent gcc-4.2 hanging on Mac. + */ + if (!tmpcand) + return NULL; + + if ( ldap_count_entries( ld, tmpcand ) > 0 ) { + return( tmpcand ); + } else { + ldap_msgfree( tmpcand ); + return( NULL ); + } +} + +/* + * ldap_ufn_setfilter - set the filter config file used in ufn searching + */ + +LDAPFiltDesc * +LDAP_CALL +ldap_ufn_setfilter( LDAP *ld, char *fname ) +{ + if ( ld->ld_filtd != NULL ) + ldap_getfilter_free( ld->ld_filtd ); + + return( ld->ld_filtd = ldap_init_getfilter( fname ) ); +} + +void +LDAP_CALL +ldap_ufn_setprefix( LDAP *ld, char *prefix ) +{ + if ( ld->ld_ufnprefix != NULL ) + NSLDAPI_FREE( ld->ld_ufnprefix ); + + ld->ld_ufnprefix = nsldapi_strdup( prefix ); +} + +int +LDAP_C +ldap_ufn_timeout( void *tvparam ) +{ + struct timeval *tv; + + tv = (struct timeval *)tvparam; + + if ( tv->tv_sec != 0 ) { + tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */ + tv->tv_sec = 0; + } + tv->tv_usec -= 100000; /* 1/10 of a second */ + + return( tv->tv_usec <= 0 ? 1 : 0 ); +} |