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