1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2011 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15/* Portions Copyright (c) 1995 Regents of the University of Michigan.
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms are permitted
19 * provided that this notice is preserved and that due credit is given
20 * to the University of Michigan at Ann Arbor. The name of the University
21 * may not be used to endorse or promote products derived from this
22 * software without specific prior written permission. This software
23 * is provided ``as is'' without express or implied warranty.
24 */
25
26#include "portable.h"
27
28#include <ac/string.h>
29
30#include "slap.h"
31#include "lutil.h"
32
33int
34modify_add_values(
35	Entry		*e,
36	Modification	*mod,
37	int		permissive,
38	const char	**text,
39	char		*textbuf,
40	size_t		textlen )
41{
42	int		rc;
43	const char	*op;
44	Attribute	*a;
45	Modification	pmod = *mod;
46
47	switch ( mod->sm_op ) {
48	case LDAP_MOD_ADD:
49		op = "add";
50		break;
51	case LDAP_MOD_REPLACE:
52		op = "replace";
53		break;
54	default:
55		op = "?";
56		assert( 0 );
57	}
58
59	/* FIXME: Catch old code that doesn't set sm_numvals.
60	 */
61	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
62		unsigned i;
63		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
64		assert( mod->sm_numvals == i );
65	}
66
67	/* check if values to add exist in attribute */
68	a = attr_find( e->e_attrs, mod->sm_desc );
69	if ( a != NULL ) {
70		MatchingRule	*mr;
71		struct berval *cvals;
72		int		rc;
73		unsigned i, p, flags;
74
75		mr = mod->sm_desc->ad_type->sat_equality;
76		if( mr == NULL || !mr->smr_match ) {
77			/* do not allow add of additional attribute
78				if no equality rule exists */
79			*text = textbuf;
80			snprintf( textbuf, textlen,
81				"modify/%s: %s: no equality matching rule",
82				op, mod->sm_desc->ad_cname.bv_val );
83			return LDAP_INAPPROPRIATE_MATCHING;
84		}
85
86		if ( permissive ) {
87			i = mod->sm_numvals;
88			pmod.sm_values = (BerVarray)ch_malloc(
89				(i + 1) * sizeof( struct berval ));
90			if ( pmod.sm_nvalues != NULL ) {
91				pmod.sm_nvalues = (BerVarray)ch_malloc(
92					(i + 1) * sizeof( struct berval ));
93			}
94		}
95
96		/* no normalization is done in this routine nor
97		 * in the matching routines called by this routine.
98		 * values are now normalized once on input to the
99		 * server (whether from LDAP or from the underlying
100		 * database).
101		 */
102		if ( a->a_desc == slap_schema.si_ad_objectClass ) {
103			/* Needed by ITS#5517 */
104			flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
105
106		} else {
107			flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
108		}
109		if ( mod->sm_nvalues ) {
110			flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
111				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
112			cvals = mod->sm_nvalues;
113		} else {
114			cvals = mod->sm_values;
115		}
116		for ( p = i = 0; i < mod->sm_numvals; i++ ) {
117			unsigned	slot;
118
119			rc = attr_valfind( a, flags, &cvals[i], &slot, NULL );
120			if ( rc == LDAP_SUCCESS ) {
121				if ( !permissive ) {
122					/* value already exists */
123					*text = textbuf;
124					snprintf( textbuf, textlen,
125						"modify/%s: %s: value #%u already exists",
126						op, mod->sm_desc->ad_cname.bv_val, i );
127					return LDAP_TYPE_OR_VALUE_EXISTS;
128				}
129			} else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
130				return rc;
131			}
132
133			if ( permissive && rc ) {
134				if ( pmod.sm_nvalues ) {
135					pmod.sm_nvalues[p] = mod->sm_nvalues[i];
136				}
137				pmod.sm_values[p++] = mod->sm_values[i];
138			}
139		}
140
141		if ( permissive ) {
142			if ( p == 0 ) {
143				/* all new values match exist */
144				ch_free( pmod.sm_values );
145				if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
146				return LDAP_SUCCESS;
147			}
148
149			BER_BVZERO( &pmod.sm_values[p] );
150			if ( pmod.sm_nvalues ) {
151				BER_BVZERO( &pmod.sm_nvalues[p] );
152			}
153		}
154	}
155
156	/* no - add them */
157	if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
158		rc = ordered_value_add( e, mod->sm_desc, a,
159			pmod.sm_values, pmod.sm_nvalues );
160	} else {
161		rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
162	}
163
164	if ( a != NULL && permissive ) {
165		ch_free( pmod.sm_values );
166		if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
167	}
168
169	if ( rc != 0 ) {
170		/* this should return result of attr_merge */
171		*text = textbuf;
172		snprintf( textbuf, textlen,
173			"modify/%s: %s: merge error (%d)",
174			op, mod->sm_desc->ad_cname.bv_val, rc );
175		return LDAP_OTHER;
176	}
177
178	return LDAP_SUCCESS;
179}
180
181int
182modify_delete_values(
183	Entry	*e,
184	Modification	*m,
185	int	perm,
186	const char	**text,
187	char *textbuf, size_t textlen )
188{
189	return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
190}
191
192int
193modify_delete_vindex(
194	Entry	*e,
195	Modification	*mod,
196	int	permissive,
197	const char	**text,
198	char *textbuf, size_t textlen,
199	int *idx )
200{
201	Attribute	*a;
202	MatchingRule 	*mr = mod->sm_desc->ad_type->sat_equality;
203	struct berval *cvals;
204	int		*id2 = NULL;
205	int		rc = 0;
206	unsigned i, j, flags;
207	char		dummy = '\0';
208
209	/*
210	 * If permissive is set, then the non-existence of an
211	 * attribute is not treated as an error.
212	 */
213
214	/* delete the entire attribute */
215	if ( mod->sm_values == NULL ) {
216		rc = attr_delete( &e->e_attrs, mod->sm_desc );
217
218		if( permissive ) {
219			rc = LDAP_SUCCESS;
220		} else if( rc != LDAP_SUCCESS ) {
221			*text = textbuf;
222			snprintf( textbuf, textlen,
223				"modify/delete: %s: no such attribute",
224				mod->sm_desc->ad_cname.bv_val );
225			rc = LDAP_NO_SUCH_ATTRIBUTE;
226		}
227		return rc;
228	}
229
230	/* FIXME: Catch old code that doesn't set sm_numvals.
231	 */
232	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
233		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
234		assert( mod->sm_numvals == i );
235	}
236	if ( !idx ) {
237		id2 = ch_malloc( mod->sm_numvals * sizeof( int ));
238		idx = id2;
239	}
240
241	if( mr == NULL || !mr->smr_match ) {
242		/* disallow specific attributes from being deleted if
243			no equality rule */
244		*text = textbuf;
245		snprintf( textbuf, textlen,
246			"modify/delete: %s: no equality matching rule",
247			mod->sm_desc->ad_cname.bv_val );
248		rc = LDAP_INAPPROPRIATE_MATCHING;
249		goto return_result;
250	}
251
252	/* delete specific values - find the attribute first */
253	if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
254		if( permissive ) {
255			rc = LDAP_SUCCESS;
256			goto return_result;
257		}
258		*text = textbuf;
259		snprintf( textbuf, textlen,
260			"modify/delete: %s: no such attribute",
261			mod->sm_desc->ad_cname.bv_val );
262		rc = LDAP_NO_SUCH_ATTRIBUTE;
263		goto return_result;
264	}
265
266	if ( a->a_desc == slap_schema.si_ad_objectClass ) {
267		/* Needed by ITS#5517,ITS#5963 */
268		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
269
270	} else {
271		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
272	}
273	if ( mod->sm_nvalues ) {
274		flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
275			| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
276		cvals = mod->sm_nvalues;
277	} else {
278		cvals = mod->sm_values;
279	}
280
281	/* Locate values to delete */
282	for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
283		unsigned sort;
284		rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
285		if ( rc == LDAP_SUCCESS ) {
286			idx[i] = sort;
287		} else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
288			if ( permissive ) {
289				idx[i] = -1;
290				continue;
291			}
292			*text = textbuf;
293			snprintf( textbuf, textlen,
294				"modify/delete: %s: no such value",
295				mod->sm_desc->ad_cname.bv_val );
296			goto return_result;
297		} else {
298			*text = textbuf;
299			snprintf( textbuf, textlen,
300				"modify/delete: %s: matching rule failed",
301				mod->sm_desc->ad_cname.bv_val );
302			goto return_result;
303		}
304	}
305
306	/* Delete the values */
307	for ( i = 0; i < mod->sm_numvals; i++ ) {
308		/* Skip permissive values that weren't found */
309		if ( idx[i] < 0 )
310			continue;
311		/* Skip duplicate delete specs */
312		if ( a->a_vals[idx[i]].bv_val == &dummy )
313			continue;
314		/* delete value and mark it as gone */
315		free( a->a_vals[idx[i]].bv_val );
316		a->a_vals[idx[i]].bv_val = &dummy;
317		if( a->a_nvals != a->a_vals ) {
318			free( a->a_nvals[idx[i]].bv_val );
319			a->a_nvals[idx[i]].bv_val = &dummy;
320		}
321		a->a_numvals--;
322	}
323
324	/* compact array skipping dummies */
325	for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
326		/* skip dummies */
327		if( a->a_vals[i].bv_val == &dummy ) {
328			assert( a->a_nvals[i].bv_val == &dummy );
329			continue;
330		}
331		if ( j != i ) {
332			a->a_vals[ j ] = a->a_vals[ i ];
333			if (a->a_nvals != a->a_vals) {
334				a->a_nvals[ j ] = a->a_nvals[ i ];
335			}
336		}
337		j++;
338	}
339
340	BER_BVZERO( &a->a_vals[j] );
341	if (a->a_nvals != a->a_vals) {
342		BER_BVZERO( &a->a_nvals[j] );
343	}
344
345	/* if no values remain, delete the entire attribute */
346	if ( !a->a_numvals ) {
347		if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
348			/* Can never happen */
349			*text = textbuf;
350			snprintf( textbuf, textlen,
351				"modify/delete: %s: no such attribute",
352				mod->sm_desc->ad_cname.bv_val );
353			rc = LDAP_NO_SUCH_ATTRIBUTE;
354		}
355	} else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
356		/* For an ordered attribute, renumber the value indices */
357		ordered_value_sort( a, 1 );
358	}
359return_result:
360	if ( id2 )
361		ch_free( id2 );
362	return rc;
363}
364
365int
366modify_replace_values(
367	Entry	*e,
368	Modification	*mod,
369	int		permissive,
370	const char	**text,
371	char *textbuf, size_t textlen )
372{
373	(void) attr_delete( &e->e_attrs, mod->sm_desc );
374
375	if ( mod->sm_values ) {
376		return modify_add_values( e, mod, permissive, text, textbuf, textlen );
377	}
378
379	return LDAP_SUCCESS;
380}
381
382int
383modify_increment_values(
384	Entry	*e,
385	Modification	*mod,
386	int	permissive,
387	const char	**text,
388	char *textbuf, size_t textlen )
389{
390	Attribute *a;
391
392	a = attr_find( e->e_attrs, mod->sm_desc );
393	if( a == NULL ) {
394		if ( permissive ) {
395			Modification modReplace = *mod;
396
397			modReplace.sm_op = LDAP_MOD_REPLACE;
398
399			return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
400		} else {
401			*text = textbuf;
402			snprintf( textbuf, textlen,
403				"modify/increment: %s: no such attribute",
404				mod->sm_desc->ad_cname.bv_val );
405			return LDAP_NO_SUCH_ATTRIBUTE;
406		}
407	}
408
409	if ( !strcmp( a->a_desc->ad_type->sat_syntax_oid, SLAPD_INTEGER_SYNTAX )) {
410		int i;
411		char str[sizeof(long)*3 + 2]; /* overly long */
412		long incr;
413
414		if ( lutil_atol( &incr, mod->sm_values[0].bv_val ) != 0 ) {
415			*text = "modify/increment: invalid syntax of increment";
416			return LDAP_INVALID_SYNTAX;
417		}
418
419		/* treat zero and errors as a no-op */
420		if( incr == 0 ) {
421			return LDAP_SUCCESS;
422		}
423
424		for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
425			char *tmp;
426			long value;
427			size_t strln;
428			if ( lutil_atol( &value, a->a_nvals[i].bv_val ) != 0 ) {
429				*text = "modify/increment: invalid syntax of original value";
430				return LDAP_INVALID_SYNTAX;
431			}
432			strln = snprintf( str, sizeof(str), "%ld", value+incr );
433
434			tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
435			if( tmp == NULL ) {
436				*text = "modify/increment: reallocation error";
437				return LDAP_OTHER;
438			}
439			a->a_nvals[i].bv_val = tmp;
440			a->a_nvals[i].bv_len = strln;
441
442			AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
443		}
444
445	} else {
446		snprintf( textbuf, textlen,
447			"modify/increment: %s: increment not supported for value syntax %s",
448			mod->sm_desc->ad_cname.bv_val,
449			a->a_desc->ad_type->sat_syntax_oid );
450		return LDAP_CONSTRAINT_VIOLATION;
451	}
452
453	return LDAP_SUCCESS;
454}
455
456void
457slap_mod_free(
458	Modification	*mod,
459	int				freeit )
460{
461	if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
462	mod->sm_values = NULL;
463
464	if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
465	mod->sm_nvalues = NULL;
466
467	if( freeit ) free( mod );
468}
469
470void
471slap_mods_free(
472    Modifications	*ml,
473    int			freevals )
474{
475	Modifications *next;
476
477	for ( ; ml != NULL; ml = next ) {
478		next = ml->sml_next;
479
480		if ( freevals )
481			slap_mod_free( &ml->sml_mod, 0 );
482		free( ml );
483	}
484}
485
486