xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/getvalues.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
1 /*
2  * The contents of this file are subject to the Netscape Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/NPL/
6  *
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  *
12  * The Original Code is Mozilla Communicator client code, released
13  * March 31, 1998.
14  *
15  * The Initial Developer of the Original Code is Netscape
16  * Communications Corporation. Portions created by Netscape are
17  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
18  * Rights Reserved.
19  *
20  * Contributor(s):
21  */
22 /*
23  *  Copyright (c) 1990 Regents of the University of Michigan.
24  *  All rights reserved.
25  */
26 /*
27  *  getvalues.c
28  */
29 
30 #if 0
31 #ifndef lint
32 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
33 #endif
34 #endif
35 
36 #include "ldap-int.h"
37 
38 
39 static void **
40 internal_ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target,
41 	int lencall )
42 {
43 	struct berelement	ber;
44 	char		        *attr;
45 	int			        rc;
46 	void			    **vals;
47 
48 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 );
49 
50 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
51 		return( NULL );	/* punt */
52 	}
53 	if ( target == NULL ||
54 	    !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) {
55 		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
56 		return( NULL );
57 	}
58 
59 	ber = *entry->lm_ber;
60 
61 	/* skip sequence, dn, sequence of, and snag the first attr */
62 	if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) {
63 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
64 		return( NULL );
65 	}
66 
67 	rc = strcasecmp( (char *)target, attr );
68 	NSLDAPI_FREE( attr );
69 	if ( rc != 0 ) {
70 		while ( 1 ) {
71 			if ( ber_scanf( &ber, "x}{a", &attr ) == LBER_ERROR ) {
72 				LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR,
73 				    NULL, NULL );
74 				return( NULL );
75 			}
76 
77 			rc = strcasecmp( (char *)target, attr );
78 			if ( rc == 0 ) {
79 				NSLDAPI_FREE( attr );
80 				break;
81 			}
82 			NSLDAPI_FREE( attr );
83 		}
84 	}
85 
86 	/*
87 	 * if we get this far, we've found the attribute and are sitting
88 	 * just before the set of values.
89 	 */
90 
91 	if ( lencall ) {
92 		rc = ber_scanf( &ber, "[V]", &vals );
93 	} else {
94 		rc = ber_scanf( &ber, "[v]", &vals );
95 	}
96 
97 	if ( rc == LBER_ERROR ) {
98 		rc = LDAP_DECODING_ERROR;
99 	} else {
100 		rc = LDAP_SUCCESS;
101 	}
102 
103 	LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
104 
105 	return(( rc == LDAP_SUCCESS ) ? vals : NULL );
106 }
107 
108 
109 /* For language-sensitive attribute matching, we are looking for a
110    language tag that looks like one of the following:
111 
112    cn
113    cn;lang-en
114    cn;lang-en-us
115    cn;lang-ja
116    cn;lang-ja-JP-kanji
117 
118    The base language specification consists of two letters following
119    "lang-". After that, there may be additional language-specific
120    narrowings preceded by a "-". In our processing we go from the
121    specific to the general, preferring a complete subtype match, but
122    accepting a partial one. For example:
123 
124    For a request for "cn;lang-en-us", we would return cn;lang-en-us
125    if present, otherwise cn;lang-en if present, otherwise cn.
126 
127    Besides the language subtype, there may be other subtypes:
128 
129    cn;lang-ja;binary         (Unlikely!)
130    cn;lang-ja;phonetic
131 
132    If not in the target, they are ignored. If they are in the target,
133    they must be in the attribute to match.
134 */
135 #define LANG_SUBTYPE_INDEX_NONE      -1
136 #define LANG_SUBTYPE_INDEX_DUPLICATE -2
137 
138 typedef struct {
139 	int    start;
140 	int    length;
141 } _SubStringIndex;
142 
143 static int
144 parse_subtypes( const char *target, int *baseLenp, char **langp,
145 				_SubStringIndex **subs, int *nsubtypes )
146 {
147 	int nSubtypes = 0;
148 	int ind = 0;
149 	char *nextToken;
150  	_SubStringIndex *result = NULL;
151 	int langIndex;
152 	int targetLen;
153 	int subtypeStart;
154 
155 	langIndex = LANG_SUBTYPE_INDEX_NONE;
156 	*subs = NULL;
157 	*langp = NULL;
158 	*baseLenp = 0;
159 	*nsubtypes = 0;
160 	targetLen = strlen( target );
161 
162 	/* Parse past base attribute */
163 	nextToken = strchr( target, ';' );
164 	if ( NULL != nextToken ) {
165 		subtypeStart = nextToken - target + 1;
166 		*baseLenp = subtypeStart - 1;
167 	}
168 	else {
169 		subtypeStart = targetLen;
170 		*baseLenp = subtypeStart;
171 	}
172 	ind = subtypeStart;
173 
174 	/* How many subtypes? */
175 	nextToken = (char *)target + subtypeStart;
176 	while ( nextToken && *nextToken ) {
177 		char *thisToken = nextToken;
178 		nextToken = strchr( thisToken, ';' );
179 		if ( NULL != nextToken )
180 			nextToken++;
181 		if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) {
182 			/* If there was a previous lang tag, this is illegal! */
183 			if ( langIndex != LANG_SUBTYPE_INDEX_NONE ) {
184 				langIndex = LANG_SUBTYPE_INDEX_DUPLICATE;
185 				return langIndex;
186 			}
187 			else {
188 				langIndex = nSubtypes;
189 			}
190 		} else {
191 			nSubtypes++;
192 		}
193 	}
194 	/* No language subtype? */
195 	if ( langIndex < 0 )
196 		return langIndex;
197 
198 	/* Allocate array of non-language subtypes */
199 	if ( nSubtypes > 0 ) {
200 		result = (_SubStringIndex *)NSLDAPI_MALLOC( sizeof(*result)
201 		    * nSubtypes );
202 		if (result == NULL) {
203 			return LANG_SUBTYPE_INDEX_NONE;	/* Error */
204 		}
205 		memset( result, 0, sizeof(*result) * nSubtypes );
206 	}
207 	ind = 0;
208 	nSubtypes = 0;
209 	ind = subtypeStart;
210 	nextToken = (char *)target + subtypeStart;
211 	while ( nextToken && *nextToken ) {
212 		char *thisToken = nextToken;
213 		int len;
214 		nextToken = strchr( thisToken, ';' );
215 		if ( NULL != nextToken ) {
216 			len = nextToken - thisToken;
217 			nextToken++;
218 		}
219 		else {
220 			nextToken = (char *)target + targetLen;
221 			len = nextToken - thisToken;
222 		}
223 		if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) {
224 			int i;
225 			*langp = (char *)NSLDAPI_MALLOC( len + 1 );
226 			if (*langp == NULL) {
227 				if (result != NULL)
228 					NSLDAPI_FREE(result);
229 				return LANG_SUBTYPE_INDEX_NONE;	/* Error */
230 			}
231 			for( i = 0; i < len; i++ )
232 				(*langp)[i] = toupper( target[ind+i] );
233 			(*langp)[len] = 0;
234 		}
235 		else {
236 			result[nSubtypes].start = thisToken - target;
237 			result[nSubtypes].length = len;
238 			nSubtypes++;
239 		}
240 	}
241 	*subs = result;
242 	*nsubtypes = nSubtypes;
243 	return langIndex;
244 }
245 
246 
247 static int
248 check_lang_match( const char *target, const char *baseTarget,
249 				  _SubStringIndex *targetTypes,
250 				  int ntargetTypes, char *targetLang, char *attr )
251 {
252 	int langIndex;
253  	_SubStringIndex *subtypes;
254 	int baseLen;
255 	char *lang;
256 	int nsubtypes;
257 	int mismatch = 0;
258 	int match = -1;
259 	int i;
260 
261 	/* Get all subtypes in the attribute name */
262  	langIndex = parse_subtypes( attr, &baseLen, &lang, &subtypes, &nsubtypes );
263 
264 	/* Check if there any required non-language subtypes which are
265 	   not in this attribute */
266 	for( i = 0; i < ntargetTypes; i++ ) {
267 		char *t = (char *)target+targetTypes[i].start;
268 		int tlen = targetTypes[i].length;
269 		int j;
270 		for( j = 0; j < nsubtypes; j++ ) {
271 			char *a = attr + subtypes[j].start;
272 			int alen = subtypes[j].length;
273 			if ( (tlen == alen) && !strncasecmp( t, a, tlen ) )
274 				break;
275 		}
276 		if ( j >= nsubtypes ) {
277 			mismatch = 1;
278 			break;
279 		}
280 	}
281 	if ( mismatch ) {
282 	    if ( NULL != subtypes )
283 		    NSLDAPI_FREE( subtypes );
284 		if ( NULL != lang )
285 		    NSLDAPI_FREE( lang );
286 		return -1;
287 	}
288 
289 	/* If there was no language subtype... */
290 	if ( langIndex < 0 ) {
291 	    if ( NULL != subtypes )
292 		    NSLDAPI_FREE( subtypes );
293 		if ( NULL != lang )
294 		    NSLDAPI_FREE( lang );
295 		if ( LANG_SUBTYPE_INDEX_NONE == langIndex )
296 			return 0;
297 		else
298 			return -1;
299 	}
300 
301 	/* Okay, now check the language subtag */
302 	i = 0;
303 	while( targetLang[i] && lang[i] &&
304 			(toupper(targetLang[i]) == toupper(lang[i])) )
305 		i++;
306 
307 	/* The total length can't be longer than the requested subtype */
308 	if ( !lang[i] || (lang[i] == ';') ) {
309 		/* If the found subtype is shorter than the requested one, the next
310 		   character in the requested one should be "-" */
311 		if ( !targetLang[i] || (targetLang[i] == '-') )
312 			match = i;
313 	}
314 	return match;
315 }
316 
317 static int check_base_match( const char *target, char *attr )
318 {
319     int i = 0;
320 	int rc;
321 	while( target[i] && attr[i] && (toupper(target[i]) == toupper(attr[i])) )
322 	    i++;
323 	rc = ( !target[i] && (!attr[i] || (';' == attr[i])) );
324 	return rc;
325 }
326 
327 static void **
328 internal_ldap_get_lang_values( LDAP *ld, LDAPMessage *entry,
329 							const char *target, char **type, int lencall )
330 {
331 	struct berelement	ber;
332 	char		        *attr = NULL;
333 	int			        rc;
334 	void			    **vals = NULL;
335 	int                 langIndex;
336  	_SubStringIndex     *subtypes;
337 	int                 nsubtypes;
338 	char                *baseTarget = NULL;
339 	int                 bestMatch = 0;
340 	char                *lang = NULL;
341 	int                 len;
342 	int					firstAttr = 1;
343 	char				*bestType = NULL;
344 
345 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 );
346 
347 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
348 		return( NULL );
349 	}
350 	if ( (target == NULL) ||
351 	    !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) {
352 		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
353 		return( NULL );
354 	}
355 
356 	/* A language check was requested, so see if there really is a
357 	   language subtype in the attribute spec */
358 	langIndex = parse_subtypes( target, &len, &lang,
359 							   &subtypes, &nsubtypes );
360 	if ( langIndex < 0 ) {
361 		if ( NULL != subtypes ) {
362 			NSLDAPI_FREE( subtypes );
363 			subtypes = NULL;
364 		}
365 		vals = internal_ldap_get_values( ld, entry, target, lencall );
366 		if ( NULL != type )
367 			*type = nsldapi_strdup( target );
368 		return vals;
369 	} else {
370 		/* Get just the base attribute name */
371 		baseTarget = (char *)NSLDAPI_MALLOC( len + 1 );
372 		if (baseTarget == NULL) {
373 			return( NULL );
374 		}
375 		memcpy( baseTarget, target, len );
376 		baseTarget[len] = 0;
377 	}
378 
379 	ber = *entry->lm_ber;
380 
381 	/* Process all attributes in the entry */
382 	while ( 1 ) {
383 		int foundMatch = 0;
384 		if ( NULL != attr )
385 			NSLDAPI_FREE( attr );
386 		if ( firstAttr ) {
387 			firstAttr = 0;
388 			/* skip sequence, dn, sequence of, and snag the first attr */
389 			if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) {
390 				break;
391 			}
392 		} else {
393 			if ( ber_scanf( &ber, "{a", &attr ) == LBER_ERROR ) {
394 				break;
395 			}
396 		}
397 
398 		if ( check_base_match( (const char *)baseTarget, attr ) ) {
399 			int thisMatch = check_lang_match( target, baseTarget,
400 											  subtypes, nsubtypes, lang, attr );
401 			if ( thisMatch > bestMatch ) {
402 				if ( vals )
403 					NSLDAPI_FREE( vals );
404 				foundMatch = 1;
405 				bestMatch = thisMatch;
406 				if ( NULL != bestType )
407 					NSLDAPI_FREE( bestType );
408 				bestType = attr;
409 				attr = NULL;
410 			}
411 		}
412 		if ( foundMatch ) {
413 			if ( lencall ) {
414 				rc = ber_scanf( &ber, "[V]}", &vals );
415 			} else {
416 				rc = ber_scanf( &ber, "[v]}", &vals );
417 			}
418 		} else {
419 			ber_scanf( &ber, "x}" );
420 		}
421 	}
422 
423 	NSLDAPI_FREE( lang );
424 	NSLDAPI_FREE( baseTarget );
425 	NSLDAPI_FREE( subtypes );
426 
427 	if ( NULL != type )
428 		*type = bestType;
429 	else if ( NULL != bestType )
430 		NSLDAPI_FREE( bestType );
431 
432 	if ( NULL == vals ) {
433 		rc = LDAP_DECODING_ERROR;
434 	} else {
435 		rc = LDAP_SUCCESS;
436 	}
437 
438 	LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
439 
440 	return( vals );
441 }
442 
443 
444 char **
445 LDAP_CALL
446 ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target )
447 {
448 	return( (char **) internal_ldap_get_values( ld, entry, target, 0 ) );
449 }
450 
451 struct berval **
452 LDAP_CALL
453 ldap_get_values_len( LDAP *ld, LDAPMessage *entry, const char *target )
454 {
455 	return( (struct berval **) internal_ldap_get_values( ld, entry, target,
456 	    1 ) );
457 }
458 
459 char **
460 LDAP_CALL
461 ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, const char *target,
462 						char **type )
463 {
464 	return( (char **) internal_ldap_get_lang_values( ld, entry,
465 											target, type, 0 ) );
466 }
467 
468 struct berval **
469 LDAP_CALL
470 ldap_get_lang_values_len( LDAP *ld, LDAPMessage *entry, const char *target,
471 						char **type )
472 {
473 	return( (struct berval **) internal_ldap_get_lang_values( ld, entry,
474 										   target, type, 1 ) );
475 }
476 
477