]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
40945fd21fbc9c5ab5c39b752eba3c00cb243b18
[openldap] / servers / slapd / mods.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2005 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
32 int
33 modify_add_values(
34         Entry           *e,
35         Modification    *mod,
36         int             permissive,
37         const char      **text,
38         char            *textbuf,
39         size_t          textlen )
40 {
41         int             rc;
42         const char      *op;
43         Attribute       *a;
44         Modification    pmod = *mod;
45
46         switch ( mod->sm_op ) {
47         case LDAP_MOD_ADD:
48                 op = "add";
49                 break;
50         case LDAP_MOD_REPLACE:
51                 op = "replace";
52                 break;
53         default:
54                 op = "?";
55                 assert( 0 );
56         }
57
58         /* check if values to add exist in attribute */
59         a = attr_find( e->e_attrs, mod->sm_desc );
60         if ( a != NULL ) {
61                 int             rc, i, j, p;
62                 MatchingRule    *mr;
63
64                 mr = mod->sm_desc->ad_type->sat_equality;
65                 if( mr == NULL || !mr->smr_match ) {
66                         /* do not allow add of additional attribute
67                                 if no equality rule exists */
68                         *text = textbuf;
69                         snprintf( textbuf, textlen,
70                                 "modify/%s: %s: no equality matching rule",
71                                 op, mod->sm_desc->ad_cname.bv_val );
72                         return LDAP_INAPPROPRIATE_MATCHING;
73                 }
74
75                 if ( permissive ) {
76                         for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
77                                 /* EMPTY -- just counting 'em */;
78                         }
79
80                         pmod.sm_values = (BerVarray)ch_malloc(
81                                 (i + 1) * sizeof( struct berval ));
82                         if ( pmod.sm_nvalues != NULL ) {
83                                 pmod.sm_nvalues = (BerVarray)ch_malloc(
84                                         (i + 1) * sizeof( struct berval ));
85                         }
86                 }
87
88                 /* no normalization is done in this routine nor
89                  * in the matching routines called by this routine. 
90                  * values are now normalized once on input to the
91                  * server (whether from LDAP or from the underlying
92                  * database).
93                  */
94                 for ( p = i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
95                         int     match;
96
97                         assert( a->a_vals[0].bv_val != NULL );
98                         for ( j = 0; !BER_BVISNULL( &a->a_vals[j] ); j++ ) {
99                                 if ( mod->sm_nvalues ) {
100                                         rc = ordered_value_match( &match, mod->sm_desc, mr,
101                                                 SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
102                                                         | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
103                                                         | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
104                                                 &a->a_nvals[j], &mod->sm_nvalues[i], text );
105                                 } else {
106                                         rc = ordered_value_match( &match, mod->sm_desc, mr,
107                                                 SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
108                                                 &a->a_vals[j], &mod->sm_values[i], text );
109                                 }
110
111                                 if ( rc == LDAP_SUCCESS && match == 0 ) {
112                                         /* value already exists */
113                                         if ( permissive ) break;
114
115                                         *text = textbuf;
116                                         snprintf( textbuf, textlen,
117                                                 "modify/%s: %s: value #%d already exists",
118                                                 op, mod->sm_desc->ad_cname.bv_val, i );
119                                         return LDAP_TYPE_OR_VALUE_EXISTS;
120
121                                 } else if ( rc != LDAP_SUCCESS ) {
122                                         return rc;
123                                 }
124                         }
125
126                         if ( permissive && match != 0 ) {
127                                 if ( pmod.sm_nvalues ) {
128                                         pmod.sm_nvalues[p] = mod->sm_nvalues[i];
129                                 }
130                                 pmod.sm_values[p++] = mod->sm_values[i];
131                         }
132                 }
133
134                 if ( permissive ) {
135                         if ( p == 0 ) {
136                                 /* all new values match exist */
137                                 ch_free( pmod.sm_values );
138                                 if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
139                                 return LDAP_SUCCESS;
140                         }
141
142                         BER_BVZERO( &pmod.sm_values[p] );
143                         if ( pmod.sm_nvalues ) {
144                                 BER_BVZERO( &pmod.sm_nvalues[p] );
145                         }
146                 }
147         }
148
149         /* no - add them */
150         if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
151                 rc = ordered_value_add( e, mod->sm_desc, a,
152                         pmod.sm_values, pmod.sm_nvalues );
153         } else {
154                 rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
155         }
156
157         if ( a != NULL && permissive ) {
158                 ch_free( pmod.sm_values );
159                 if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
160         }
161
162         if ( rc != 0 ) {
163                 /* this should return result of attr_merge */
164                 *text = textbuf;
165                 snprintf( textbuf, textlen,
166                         "modify/%s: %s: merge error",
167                         op, mod->sm_desc->ad_cname.bv_val );
168                 return LDAP_OTHER;
169         }
170
171         return LDAP_SUCCESS;
172 }
173
174 int
175 modify_delete_values(
176         Entry   *e,
177         Modification    *m,
178         int     perm,
179         const char      **text,
180         char *textbuf, size_t textlen )
181 {
182         return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
183 }
184
185 int
186 modify_delete_vindex(
187         Entry   *e,
188         Modification    *mod,
189         int     permissive,
190         const char      **text,
191         char *textbuf, size_t textlen,
192         int *idx )
193 {
194         int             i, j, k, rc = LDAP_SUCCESS;
195         Attribute       *a;
196         MatchingRule    *mr = mod->sm_desc->ad_type->sat_equality;
197         char            dummy = '\0';
198         int             match = 0;
199
200         /*
201          * If permissive is set, then the non-existence of an 
202          * attribute is not treated as an error.
203          */
204
205         /* delete the entire attribute */
206         if ( mod->sm_values == NULL ) {
207                 rc = attr_delete( &e->e_attrs, mod->sm_desc );
208
209                 if( permissive ) {
210                         rc = LDAP_SUCCESS;
211                 } else if( rc != LDAP_SUCCESS ) {
212                         *text = textbuf;
213                         snprintf( textbuf, textlen,
214                                 "modify/delete: %s: no such attribute",
215                                 mod->sm_desc->ad_cname.bv_val );
216                         rc = LDAP_NO_SUCH_ATTRIBUTE;
217                 }
218                 return rc;
219         }
220
221         if( mr == NULL || !mr->smr_match ) {
222                 /* disallow specific attributes from being deleted if
223                         no equality rule */
224                 *text = textbuf;
225                 snprintf( textbuf, textlen,
226                         "modify/delete: %s: no equality matching rule",
227                         mod->sm_desc->ad_cname.bv_val );
228                 return LDAP_INAPPROPRIATE_MATCHING;
229         }
230
231         /* delete specific values - find the attribute first */
232         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
233                 if( permissive ) {
234                         return LDAP_SUCCESS;
235                 }
236                 *text = textbuf;
237                 snprintf( textbuf, textlen,
238                         "modify/delete: %s: no such attribute",
239                         mod->sm_desc->ad_cname.bv_val );
240                 return LDAP_NO_SUCH_ATTRIBUTE;
241         }
242
243         for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
244                 int     found = 0;
245                 for ( j = 0; !BER_BVISNULL( &a->a_vals[j] ); j++ ) {
246                         /* skip already deleted values */
247                         if ( a->a_vals[j].bv_val == &dummy ) {
248                                 continue;
249                         }
250
251                         if( mod->sm_nvalues ) {
252                                 assert( a->a_nvals != NULL );
253                                 rc = ordered_value_match( &match, a->a_desc, mr,
254                                         SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
255                                                 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
256                                                 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
257                                         &a->a_nvals[j], &mod->sm_nvalues[i], text );
258                         } else {
259 #if 0
260                                 assert( a->a_nvals == NULL );
261 #endif
262                                 rc = ordered_value_match( &match, a->a_desc, mr,
263                                         SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
264                                         &a->a_vals[j], &mod->sm_values[i], text );
265                         }
266
267                         if ( rc != LDAP_SUCCESS ) {
268                                 *text = textbuf;
269                                 snprintf( textbuf, textlen,
270                                         "%s: matching rule failed",
271                                         mod->sm_desc->ad_cname.bv_val );
272                                 break;
273                         }
274
275                         if ( match != 0 ) {
276                                 continue;
277                         }
278
279                         found = 1;
280
281                         if ( idx )
282                                 idx[i] = j;
283
284                         /* delete value and mark it as dummy */
285                         free( a->a_vals[j].bv_val );
286                         a->a_vals[j].bv_val = &dummy;
287                         if( a->a_nvals != a->a_vals ) {
288                                 free( a->a_nvals[j].bv_val );
289                                 a->a_nvals[j].bv_val = &dummy;
290                         }
291
292                         break;
293                 }
294
295                 if ( found == 0 ) {
296                         *text = textbuf;
297                         snprintf( textbuf, textlen,
298                                 "modify/delete: %s: no such value",
299                                 mod->sm_desc->ad_cname.bv_val );
300                         rc = LDAP_NO_SUCH_ATTRIBUTE;
301                         if ( i > 0 ) {
302                                 break;
303                         } else {
304                                 goto return_results;
305                         }
306                 }
307         }
308
309         /* compact array skipping dummies */
310         for ( k = 0, j = 0; !BER_BVISNULL( &a->a_vals[k] ); k++ ) {
311                 /* skip dummies */
312                 if( a->a_vals[k].bv_val == &dummy ) {
313                         assert( a->a_nvals[k].bv_val == &dummy );
314                         continue;
315                 }
316                 if ( j != k ) {
317                         a->a_vals[ j ] = a->a_vals[ k ];
318                         if (a->a_nvals != a->a_vals) {
319                                 a->a_nvals[ j ] = a->a_nvals[ k ];
320                         }
321                 }
322
323                 j++;
324         }
325
326         BER_BVZERO( &a->a_vals[j] );
327         if (a->a_nvals != a->a_vals) {
328                 BER_BVZERO( &a->a_nvals[j] );
329         }
330
331         /* if no values remain, delete the entire attribute */
332         if ( BER_BVISNULL( &a->a_vals[0] ) ) {
333                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
334                         *text = textbuf;
335                         snprintf( textbuf, textlen,
336                                 "modify/delete: %s: no such attribute",
337                                 mod->sm_desc->ad_cname.bv_val );
338                         rc = LDAP_NO_SUCH_ATTRIBUTE;
339                 }
340         } else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
341                 /* For an ordered attribute, renumber the value indices */
342                 ordered_value_sort( a, 1 );
343         }
344
345 return_results:;
346
347         return rc;
348 }
349
350 int
351 modify_replace_values(
352         Entry   *e,
353         Modification    *mod,
354         int             permissive,
355         const char      **text,
356         char *textbuf, size_t textlen )
357 {
358         (void) attr_delete( &e->e_attrs, mod->sm_desc );
359
360         if ( mod->sm_values ) {
361                 return modify_add_values( e, mod, permissive, text, textbuf, textlen );
362         }
363
364         return LDAP_SUCCESS;
365 }
366
367 int
368 modify_increment_values(
369         Entry   *e,
370         Modification    *mod,
371         int     permissive,
372         const char      **text,
373         char *textbuf, size_t textlen )
374 {
375         Attribute *a;
376
377         a = attr_find( e->e_attrs, mod->sm_desc );
378         if( a == NULL ) {
379                 *text = textbuf;
380                 snprintf( textbuf, textlen,
381                         "modify/increment: %s: no such attribute",
382                         mod->sm_desc->ad_cname.bv_val );
383                 return LDAP_NO_SUCH_ATTRIBUTE;
384         }
385
386         if ( !strcmp( a->a_desc->ad_type->sat_syntax_oid, SLAPD_INTEGER_SYNTAX )) {
387                 int i;
388                 char str[sizeof(long)*3 + 2]; /* overly long */
389                 long incr = atol( mod->sm_values[0].bv_val );
390
391                 /* treat zero and errors as a no-op */
392                 if( incr == 0 ) {
393                         return LDAP_SUCCESS;
394                 }
395
396                 for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
397                         char *tmp;
398                         long value = atol( a->a_nvals[i].bv_val );
399                         size_t strln = snprintf( str, sizeof(str), "%ld", value+incr );
400
401                         tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
402                         if( tmp == NULL ) {
403                                 *text = "modify/increment: reallocation error";
404                                 return LDAP_OTHER;;
405                         }
406                         a->a_nvals[i].bv_val = tmp;
407                         a->a_nvals[i].bv_len = strln;
408
409                         AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
410                 }
411
412         } else {
413                 snprintf( textbuf, textlen,
414                         "modify/increment: %s: increment not supported for value syntax %s",
415                         mod->sm_desc->ad_cname.bv_val,
416                         a->a_desc->ad_type->sat_syntax_oid );
417                 return LDAP_CONSTRAINT_VIOLATION;
418         }
419
420         return LDAP_SUCCESS;
421 }
422
423 void
424 slap_mod_free(
425         Modification    *mod,
426         int                             freeit )
427 {
428         if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
429         mod->sm_values = NULL;
430
431         if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
432         mod->sm_nvalues = NULL;
433
434         if( freeit ) free( mod );
435 }
436
437 void
438 slap_mods_free(
439     Modifications       *ml,
440     int                 freevals )
441 {
442         Modifications *next;
443
444         for ( ; ml != NULL; ml = next ) {
445                 next = ml->sml_next;
446
447                 if ( freevals )
448                         slap_mod_free( &ml->sml_mod, 0 );
449                 free( ml );
450         }
451 }
452