#pragma ident "%Z%%M% %I% %E% SMI" /* * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Netscape are * Copyright (C) 1998-1999 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* * Copyright (c) 1990 Regents of the University of Michigan. * All rights reserved. */ /* * getvalues.c */ #if 0 #ifndef lint static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; #endif #endif #include "ldap-int.h" static void ** internal_ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target, int lencall ) { struct berelement ber; char *attr; int rc; void **vals; LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 ); if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { return( NULL ); /* punt */ } if ( target == NULL || !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); return( NULL ); } ber = *entry->lm_ber; /* skip sequence, dn, sequence of, and snag the first attr */ if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) { LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); return( NULL ); } rc = strcasecmp( (char *)target, attr ); NSLDAPI_FREE( attr ); if ( rc != 0 ) { while ( 1 ) { if ( ber_scanf( &ber, "x}{a", &attr ) == LBER_ERROR ) { LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); return( NULL ); } rc = strcasecmp( (char *)target, attr ); if ( rc == 0 ) { NSLDAPI_FREE( attr ); break; } NSLDAPI_FREE( attr ); } } /* * if we get this far, we've found the attribute and are sitting * just before the set of values. */ if ( lencall ) { rc = ber_scanf( &ber, "[V]", &vals ); } else { rc = ber_scanf( &ber, "[v]", &vals ); } if ( rc == LBER_ERROR ) { rc = LDAP_DECODING_ERROR; } else { rc = LDAP_SUCCESS; } LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); return(( rc == LDAP_SUCCESS ) ? vals : NULL ); } /* For language-sensitive attribute matching, we are looking for a language tag that looks like one of the following: cn cn;lang-en cn;lang-en-us cn;lang-ja cn;lang-ja-JP-kanji The base language specification consists of two letters following "lang-". After that, there may be additional language-specific narrowings preceded by a "-". In our processing we go from the specific to the general, preferring a complete subtype match, but accepting a partial one. For example: For a request for "cn;lang-en-us", we would return cn;lang-en-us if present, otherwise cn;lang-en if present, otherwise cn. Besides the language subtype, there may be other subtypes: cn;lang-ja;binary (Unlikely!) cn;lang-ja;phonetic If not in the target, they are ignored. If they are in the target, they must be in the attribute to match. */ #define LANG_SUBTYPE_INDEX_NONE -1 #define LANG_SUBTYPE_INDEX_DUPLICATE -2 typedef struct { int start; int length; } _SubStringIndex; static int parse_subtypes( const char *target, int *baseLenp, char **langp, _SubStringIndex **subs, int *nsubtypes ) { int nSubtypes = 0; int ind = 0; char *nextToken; _SubStringIndex *result = NULL; int langIndex; int targetLen; int subtypeStart; langIndex = LANG_SUBTYPE_INDEX_NONE; *subs = NULL; *langp = NULL; *baseLenp = 0; *nsubtypes = 0; targetLen = strlen( target ); /* Parse past base attribute */ nextToken = strchr( target, ';' ); if ( NULL != nextToken ) { subtypeStart = nextToken - target + 1; *baseLenp = subtypeStart - 1; } else { subtypeStart = targetLen; *baseLenp = subtypeStart; } ind = subtypeStart; /* How many subtypes? */ nextToken = (char *)target + subtypeStart; while ( nextToken && *nextToken ) { char *thisToken = nextToken; nextToken = strchr( thisToken, ';' ); if ( NULL != nextToken ) nextToken++; if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) { /* If there was a previous lang tag, this is illegal! */ if ( langIndex != LANG_SUBTYPE_INDEX_NONE ) { langIndex = LANG_SUBTYPE_INDEX_DUPLICATE; return langIndex; } else { langIndex = nSubtypes; } } else { nSubtypes++; } } /* No language subtype? */ if ( langIndex < 0 ) return langIndex; /* Allocate array of non-language subtypes */ if ( nSubtypes > 0 ) { result = (_SubStringIndex *)NSLDAPI_MALLOC( sizeof(*result) * nSubtypes ); if (result == NULL) { return LANG_SUBTYPE_INDEX_NONE; /* Error */ } memset( result, 0, sizeof(*result) * nSubtypes ); } ind = 0; nSubtypes = 0; ind = subtypeStart; nextToken = (char *)target + subtypeStart; while ( nextToken && *nextToken ) { char *thisToken = nextToken; int len; nextToken = strchr( thisToken, ';' ); if ( NULL != nextToken ) { len = nextToken - thisToken; nextToken++; } else { nextToken = (char *)target + targetLen; len = nextToken - thisToken; } if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) { int i; *langp = (char *)NSLDAPI_MALLOC( len + 1 ); if (*langp == NULL) { if (result != NULL) NSLDAPI_FREE(result); return LANG_SUBTYPE_INDEX_NONE; /* Error */ } for( i = 0; i < len; i++ ) (*langp)[i] = toupper( target[ind+i] ); (*langp)[len] = 0; } else { result[nSubtypes].start = thisToken - target; result[nSubtypes].length = len; nSubtypes++; } } *subs = result; *nsubtypes = nSubtypes; return langIndex; } static int check_lang_match( const char *target, const char *baseTarget, _SubStringIndex *targetTypes, int ntargetTypes, char *targetLang, char *attr ) { int langIndex; _SubStringIndex *subtypes; int baseLen; char *lang; int nsubtypes; int mismatch = 0; int match = -1; int i; /* Get all subtypes in the attribute name */ langIndex = parse_subtypes( attr, &baseLen, &lang, &subtypes, &nsubtypes ); /* Check if there any required non-language subtypes which are not in this attribute */ for( i = 0; i < ntargetTypes; i++ ) { char *t = (char *)target+targetTypes[i].start; int tlen = targetTypes[i].length; int j; for( j = 0; j < nsubtypes; j++ ) { char *a = attr + subtypes[j].start; int alen = subtypes[j].length; if ( (tlen == alen) && !strncasecmp( t, a, tlen ) ) break; } if ( j >= nsubtypes ) { mismatch = 1; break; } } if ( mismatch ) { if ( NULL != subtypes ) NSLDAPI_FREE( subtypes ); if ( NULL != lang ) NSLDAPI_FREE( lang ); return -1; } /* If there was no language subtype... */ if ( langIndex < 0 ) { if ( NULL != subtypes ) NSLDAPI_FREE( subtypes ); if ( NULL != lang ) NSLDAPI_FREE( lang ); if ( LANG_SUBTYPE_INDEX_NONE == langIndex ) return 0; else return -1; } /* Okay, now check the language subtag */ i = 0; while( targetLang[i] && lang[i] && (toupper(targetLang[i]) == toupper(lang[i])) ) i++; /* The total length can't be longer than the requested subtype */ if ( !lang[i] || (lang[i] == ';') ) { /* If the found subtype is shorter than the requested one, the next character in the requested one should be "-" */ if ( !targetLang[i] || (targetLang[i] == '-') ) match = i; } return match; } static int check_base_match( const char *target, char *attr ) { int i = 0; int rc; while( target[i] && attr[i] && (toupper(target[i]) == toupper(attr[i])) ) i++; rc = ( !target[i] && (!attr[i] || (';' == attr[i])) ); return rc; } static void ** internal_ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, const char *target, char **type, int lencall ) { struct berelement ber; char *attr = NULL; int rc; void **vals = NULL; int langIndex; _SubStringIndex *subtypes; int nsubtypes; char *baseTarget = NULL; int bestMatch = 0; char *lang = NULL; int len; int firstAttr = 1; char *bestType = NULL; LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 ); if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { return( NULL ); } if ( (target == NULL) || !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); return( NULL ); } /* A language check was requested, so see if there really is a language subtype in the attribute spec */ langIndex = parse_subtypes( target, &len, &lang, &subtypes, &nsubtypes ); if ( langIndex < 0 ) { if ( NULL != subtypes ) { NSLDAPI_FREE( subtypes ); subtypes = NULL; } vals = internal_ldap_get_values( ld, entry, target, lencall ); if ( NULL != type ) *type = nsldapi_strdup( target ); return vals; } else { /* Get just the base attribute name */ baseTarget = (char *)NSLDAPI_MALLOC( len + 1 ); if (baseTarget == NULL) { return( NULL ); } memcpy( baseTarget, target, len ); baseTarget[len] = 0; } ber = *entry->lm_ber; /* Process all attributes in the entry */ while ( 1 ) { int foundMatch = 0; if ( NULL != attr ) NSLDAPI_FREE( attr ); if ( firstAttr ) { firstAttr = 0; /* skip sequence, dn, sequence of, and snag the first attr */ if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) { break; } } else { if ( ber_scanf( &ber, "{a", &attr ) == LBER_ERROR ) { break; } } if ( check_base_match( (const char *)baseTarget, attr ) ) { int thisMatch = check_lang_match( target, baseTarget, subtypes, nsubtypes, lang, attr ); if ( thisMatch > bestMatch ) { if ( vals ) NSLDAPI_FREE( vals ); foundMatch = 1; bestMatch = thisMatch; if ( NULL != bestType ) NSLDAPI_FREE( bestType ); bestType = attr; attr = NULL; } } if ( foundMatch ) { if ( lencall ) { rc = ber_scanf( &ber, "[V]}", &vals ); } else { rc = ber_scanf( &ber, "[v]}", &vals ); } } else { ber_scanf( &ber, "x}" ); } } NSLDAPI_FREE( lang ); NSLDAPI_FREE( baseTarget ); NSLDAPI_FREE( subtypes ); if ( NULL != type ) *type = bestType; else if ( NULL != bestType ) NSLDAPI_FREE( bestType ); if ( NULL == vals ) { rc = LDAP_DECODING_ERROR; } else { rc = LDAP_SUCCESS; } LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); return( vals ); } char ** LDAP_CALL ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target ) { return( (char **) internal_ldap_get_values( ld, entry, target, 0 ) ); } struct berval ** LDAP_CALL ldap_get_values_len( LDAP *ld, LDAPMessage *entry, const char *target ) { return( (struct berval **) internal_ldap_get_values( ld, entry, target, 1 ) ); } char ** LDAP_CALL ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, const char *target, char **type ) { return( (char **) internal_ldap_get_lang_values( ld, entry, target, type, 0 ) ); } struct berval ** LDAP_CALL ldap_get_lang_values_len( LDAP *ld, LDAPMessage *entry, const char *target, char **type ) { return( (struct berval **) internal_ldap_get_lang_values( ld, entry, target, type, 1 ) ); }