1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 
3 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
4  *
5  * The contents of this file are subject to the Netscape Public License
6  * Version 1.0 (the "NPL"); you may not use this file except in
7  * compliance with the NPL.  You may obtain a copy of the NPL at
8  * http://www.mozilla.org/NPL/
9  *
10  * Software distributed under the NPL is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
12  * for the specific language governing rights and limitations under the
13  * NPL.
14  *
15  * The Initial Developer of this code under the NPL is Netscape
16  * Communications Corporation.  Portions created by Netscape are
17  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
18  * Reserved.
19  */
20 #include "ldap-int.h"
21 
22 /* ldap_create_sort_control:
23 
24    Parameters are
25 
26    ld              LDAP pointer to the desired connection
27 
28    sortKeyList     an array of sortkeys
29 
30    ctl_iscritical  Indicates whether the control is critical of not. If
31                    this field is non-zero, the operation will only be car-
32                    ried out if the control is recognized by the server
33                    and/or client
34 
35    ctrlp           the address of a place to put the constructed control
36 */
37 
38 int
39 LDAP_CALL
40 ldap_create_sort_control (
41      LDAP *ld,
42      LDAPsortkey **sortKeyList,
43      const char ctl_iscritical,
44      LDAPControl **ctrlp
45 )
46 {
47 	BerElement		*ber;
48 	int				i, rc;
49 
50 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
51 		return( LDAP_PARAM_ERROR );
52 	}
53 
54 	if ( sortKeyList == NULL || ctrlp == NULL ) {
55 		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
56 		return ( LDAP_PARAM_ERROR );
57 	}
58 
59 	/* create a ber package to hold the controlValue */
60 	if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) != LDAP_SUCCESS ) {
61 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
62 		return( LDAP_NO_MEMORY );
63 	}
64 
65 	/* encode the start of the sequence of sequences into the ber */
66 	if ( ber_printf( ber, "{" ) == -1 ) {
67 		goto encoding_error_exit;
68 	}
69 
70 	/* the sort control value will be encoded as a sequence of sequences
71 	   which are each encoded as one of the following: {s} or {sts} or {stb} or {ststb}
72 	   since the orderingRule and reverseOrder flag are both optional */
73 	for ( i = 0; sortKeyList[i] != NULL; i++ ) {
74 
75 		/* encode the attributeType into the ber */
76 		if ( ber_printf( ber, "{s", (sortKeyList[i])->sk_attrtype  )
77 		    == -1 ) {
78 			goto encoding_error_exit;
79 		}
80 
81 		/* encode the optional orderingRule into the ber */
82 		if ( (sortKeyList[i])->sk_matchruleoid != NULL ) {
83 			if ( ber_printf( ber, "ts", LDAP_TAG_SK_MATCHRULE,
84 			    (sortKeyList[i])->sk_matchruleoid )
85 			    == -1 ) {
86 				goto encoding_error_exit;
87 			}
88 		}
89 
90 		/* Encode the optional reverseOrder flag into the ber. */
91 		/* If the flag is false, it should be absent. */
92 		if ( (sortKeyList[i])->sk_reverseorder ) {
93 			if ( ber_printf( ber, "tb}", LDAP_TAG_SK_REVERSE,
94 			    (sortKeyList[i])->sk_reverseorder ) == -1 ) {
95 				goto encoding_error_exit;
96 			}
97 		} else {
98 			if ( ber_printf( ber, "}" ) == -1 ) {
99 				goto encoding_error_exit;
100 			}
101 		}
102 	}
103 
104 	/* encode the end of the sequence of sequences into the ber */
105 	if ( ber_printf( ber, "}" ) == -1 ) {
106 		goto encoding_error_exit;
107 	}
108 
109 	rc = nsldapi_build_control( LDAP_CONTROL_SORTREQUEST, ber, 1,
110 	    ctl_iscritical, ctrlp );
111 
112 	LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
113 	return( rc );
114 
115 encoding_error_exit:
116 	LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
117 	ber_free( ber, 1 );
118 	return( LDAP_ENCODING_ERROR );
119 }
120 
121 /* ldap_parse_sort_control:
122 
123    Parameters are
124 
125    ld              LDAP pointer to the desired connection
126 
127    ctrlp           An array of controls obtained from calling
128                    ldap_parse_result on the set of results returned by
129                    the server
130 
131    result          the address of a place to put the result code
132 
133    attribute       the address of a place to put the name of the
134                    attribute which cause the operation to fail, optionally
135                    returned by the server */
136 
137 int
138 LDAP_CALL
139 ldap_parse_sort_control (
140      LDAP *ld,
141      LDAPControl **ctrlp,
142      unsigned long *result,
143      char **attribute
144 )
145 {
146 	BerElement *ber;
147 	int i, foundSortControl;
148 	LDAPControl *sortCtrlp;
149 	ber_len_t len;
150 	ber_tag_t tag;
151 	char *attr;
152 
153 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || result == NULL ||
154 		attribute == NULL ) {
155 	    return( LDAP_PARAM_ERROR );
156 	}
157 
158 
159 	/* find the sortControl in the list of controls if it exists */
160 	if ( ctrlp == NULL ) {
161 		LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL );
162 		return ( LDAP_CONTROL_NOT_FOUND );
163 	}
164 	foundSortControl = 0;
165 	for ( i = 0; (( ctrlp[i] != NULL ) && ( !foundSortControl )); i++ ) {
166 		foundSortControl = !strcmp( ctrlp[i]->ldctl_oid, LDAP_CONTROL_SORTRESPONSE );
167 	}
168 	if ( !foundSortControl ) {
169 		LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL );
170 		return ( LDAP_CONTROL_NOT_FOUND );
171 	} else {
172 		/* let local var point to the sortControl */
173 		sortCtrlp = ctrlp[i-1];
174 	}
175 
176 	/*  allocate a Ber element with the contents of the sort_control's struct berval */
177 	if ( ( ber = ber_init( &sortCtrlp->ldctl_value ) ) == NULL ) {
178 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
179 		return( LDAP_NO_MEMORY );
180 	}
181 
182 	/* decode the result from the Berelement */
183 	if ( ber_scanf( ber, "{i", result ) == LBER_ERROR ) {
184 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
185 		ber_free( ber, 1 );
186 		return( LDAP_DECODING_ERROR );
187 	}
188 
189 	/* if the server returned one, decode the attribute from the Ber element */
190 	if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SR_ATTRTYPE ) {
191 		if ( ber_scanf( ber, "ta", &tag, &attr ) == LBER_ERROR ) {
192 			LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
193 			ber_free( ber, 1 );
194 			return( LDAP_DECODING_ERROR );
195 		}
196 		*attribute = attr;
197 	} else {
198 		*attribute = NULL;
199 	}
200 
201 	if ( ber_scanf( ber, "}" ) == LBER_ERROR ) {
202 		LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
203 		ber_free( ber, 1 );
204 		return( LDAP_DECODING_ERROR );
205 	}
206 
207 	/* the ber encoding is no longer needed */
208 	ber_free(ber,1);
209 
210 	return( LDAP_SUCCESS );
211 }
212 
213 /* Routines for the manipulation of string-representations of sort control keylists */
214 
215 static int count_tokens(const char *s)
216 {
217 	int count = 0;
218 	const char *p = s;
219 	int whitespace = 1;
220 	/* Loop along the string counting the number of times we see the
221 	 * beginning of non-whitespace. This tells us
222 	 * the number of tokens in the string
223 	 */
224 	while (*p != '\0') {
225 		if (whitespace) {
226 			if (!isspace(*p)) {
227 				whitespace = 0;
228 				count++;
229 			}
230 		} else {
231 			if (isspace(*p)) {
232 				whitespace = 1;
233 			}
234 		}
235 		p++;
236 	}
237 	return count;
238 }
239 
240 
241 static int read_next_token(const char **s,LDAPsortkey **key)
242 {
243 	char c = 0;
244 	const char *pos = *s;
245 	int retval = 0;
246 	LDAPsortkey *new_key = NULL;
247 
248 	const char *matchrule_source = NULL;
249 	int matchrule_size = 0;
250 	const char *attrdesc_source = NULL;
251 	int attrdesc_size = 0;
252 	int reverse = 0;
253 
254 	int state = 0;
255 
256 	while ( ((c = *pos++) != '\0') && (state != 4) ) {
257 		switch (state) {
258 		case 0:
259 		/* case where we've not seen the beginning of the attr yet */
260 			/* If we still see whitespace, nothing to do */
261 			if (!isspace(c)) {
262 				/* Otherwise, something to look at */
263 				/* Is it a minus sign ? */
264 				if ('-' == c) {
265 					reverse = 1;
266 				} else {
267 					attrdesc_source = pos - 1;
268 					state = 1;
269 				}
270 			}
271 			break;
272 		case 1:
273 		/* case where we've seen the beginning of the attr, but not the end */
274 			/* Is this char either whitespace or a ';' ? */
275 			if ( isspace(c) || (':' == c)) {
276 				attrdesc_size = (pos - attrdesc_source) - 1;
277 				if (':' == c) {
278 					state = 2;
279 				} else {
280 					state = 4;
281 				}
282 			}
283 			break;
284 		case 2:
285 		/* case where we've seen the end of the attr and want the beginning of match rule */
286 			if (!isspace(c)) {
287 				matchrule_source = pos - 1;
288 				state = 3;
289 			} else {
290 				state = 4;
291 			}
292 			break;
293 		case 3:
294 		/* case where we've seen the beginning of match rule and want to find the end */
295 			if (isspace(c)) {
296 				matchrule_size = (pos - matchrule_source) - 1;
297 				state = 4;
298 			}
299 			break;
300 		default:
301 			break;
302 		}
303 	}
304 
305 	if (3 == state) {
306 		/* means we fell off the end of the string looking for the end of the marching rule */
307 		matchrule_size = (pos - matchrule_source) - 1;
308 	}
309 
310 	if (1 == state) {
311 		/* means we fell of the end of the string looking for the end of the attribute */
312 		attrdesc_size = (pos - attrdesc_source) - 1;
313 	}
314 
315 	if (NULL == attrdesc_source)  {
316 		/* Didn't find anything */
317 		return -1;
318 	}
319 
320 	new_key = (LDAPsortkey*)NSLDAPI_MALLOC(sizeof(LDAPsortkey));
321 	if (0 == new_key) {
322 		return LDAP_NO_MEMORY;
323 	}
324 
325 	/* Allocate the strings */
326 	new_key->sk_attrtype = (char *)NSLDAPI_MALLOC(attrdesc_size + 1);
327 	if (NULL != matchrule_source) {
328 		new_key->sk_matchruleoid = (char *)NSLDAPI_MALLOC(
329 		    matchrule_size + 1);
330 	} else {
331 		new_key->sk_matchruleoid = NULL;
332 	}
333 	/* Copy over the strings */
334 	memcpy(new_key->sk_attrtype,attrdesc_source,attrdesc_size);
335 	*(new_key->sk_attrtype + attrdesc_size) = '\0';
336 	if (NULL != matchrule_source) {
337 		memcpy(new_key->sk_matchruleoid,matchrule_source,matchrule_size);
338 		*(new_key->sk_matchruleoid + matchrule_size) = '\0';
339 	}
340 
341 	new_key->sk_reverseorder = reverse;
342 
343 	*s = pos - 1;
344 	*key = new_key;
345 	return retval;
346 }
347 
348 int
349 LDAP_CALL
350 ldap_create_sort_keylist (
351 	LDAPsortkey ***sortKeyList,
352 	const char *string_rep
353 )
354 {
355 	int count = 0;
356 	LDAPsortkey **pointer_array = NULL;
357 	const char *current_position = NULL;
358 	int retval = 0;
359 	int i = 0;
360 
361 	/* Figure out how many there are */
362 	if (NULL == string_rep) {
363 		return LDAP_PARAM_ERROR;
364 	}
365 	if (NULL == sortKeyList) {
366 		return LDAP_PARAM_ERROR;
367 	}
368 	count = count_tokens(string_rep);
369 	if (0 == count) {
370 		*sortKeyList = NULL;
371 		return LDAP_PARAM_ERROR;
372 	}
373 	/* Allocate enough memory for the pointers */
374 	pointer_array = (LDAPsortkey**)NSLDAPI_MALLOC(sizeof(LDAPsortkey*)
375 	    * (count + 1) );
376 	if (NULL == pointer_array) {
377 		return LDAP_NO_MEMORY;
378 	}
379 	/* Now walk along the string, allocating and filling in the LDAPsearchkey structure */
380 	current_position = string_rep;
381 
382 	for (i = 0; i < count; i++) {
383 		if (0 != (retval = read_next_token(&current_position,&(pointer_array[i])))) {
384 			pointer_array[count] = NULL;
385 			ldap_free_sort_keylist(pointer_array);
386 			*sortKeyList = NULL;
387 			return retval;
388 		}
389 	}
390 	pointer_array[count] = NULL;
391 	*sortKeyList = pointer_array;
392 	return LDAP_SUCCESS;
393 }
394 
395 void
396 LDAP_CALL
397 ldap_free_sort_keylist (
398 	LDAPsortkey **sortKeyList
399 )
400 {
401 	LDAPsortkey *this_one = NULL;
402 	int i = 0;
403 
404 	if ( NULL == sortKeyList ) {
405 		return;
406 	}
407 
408 	/* Walk down the list freeing the LDAPsortkey structures */
409 	for (this_one = sortKeyList[0]; this_one ; this_one = sortKeyList[++i]) {
410 		/* Free the strings, if present */
411 		if (NULL != this_one->sk_attrtype) {
412 			NSLDAPI_FREE(this_one->sk_attrtype);
413 		}
414 		if (NULL != this_one->sk_matchruleoid) {
415 			NSLDAPI_FREE(this_one->sk_matchruleoid);
416 		}
417 		NSLDAPI_FREE(this_one);
418 	}
419 	/* Free the pointer list */
420 	NSLDAPI_FREE(sortKeyList);
421 }
422