]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
1ad15f8906997ed3068939a25d4a1237cc16e0c6
[openldap] / servers / slapd / mods.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2009 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
33 int
34 modify_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
181 int
182 modify_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
192 int
193 modify_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 ( mod->sm_nvalues ) {
267                 flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
268                         | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
269                         | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
270                 cvals = mod->sm_nvalues;
271         } else {
272                 flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
273                 cvals = mod->sm_values;
274         }
275
276         /* Locate values to delete */
277         for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
278                 unsigned sort;
279                 rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
280                 if ( rc == LDAP_SUCCESS ) {
281                         idx[i] = sort;
282                 } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
283                         if ( permissive ) {
284                                 idx[i] = -1;
285                                 continue;
286                         }
287                         *text = textbuf;
288                         snprintf( textbuf, textlen,
289                                 "modify/delete: %s: no such value",
290                                 mod->sm_desc->ad_cname.bv_val );
291                         goto return_result;
292                 } else {
293                         *text = textbuf;
294                         snprintf( textbuf, textlen,
295                                 "modify/delete: %s: matching rule failed",
296                                 mod->sm_desc->ad_cname.bv_val );
297                         goto return_result;
298                 }
299         }
300
301         /* Delete the values */
302         for ( i = 0; i < mod->sm_numvals; i++ ) {
303                 /* Skip permissive values that weren't found */
304                 if ( idx[i] < 0 )
305                         continue;
306                 /* Skip duplicate delete specs */
307                 if ( a->a_vals[idx[i]].bv_val == &dummy )
308                         continue;
309                 /* delete value and mark it as gone */
310                 free( a->a_vals[idx[i]].bv_val );
311                 a->a_vals[idx[i]].bv_val = &dummy;
312                 if( a->a_nvals != a->a_vals ) {
313                         free( a->a_nvals[idx[i]].bv_val );
314                         a->a_nvals[idx[i]].bv_val = &dummy;
315                 }
316                 a->a_numvals--;
317         }
318
319         /* compact array skipping dummies */
320         for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
321                 /* skip dummies */
322                 if( a->a_vals[i].bv_val == &dummy ) {
323                         assert( a->a_nvals[i].bv_val == &dummy );
324                         continue;
325                 }
326                 if ( j != i ) {
327                         a->a_vals[ j ] = a->a_vals[ i ];
328                         if (a->a_nvals != a->a_vals) {
329                                 a->a_nvals[ j ] = a->a_nvals[ i ];
330                         }
331                 }
332                 j++;
333         }
334
335         BER_BVZERO( &a->a_vals[j] );
336         if (a->a_nvals != a->a_vals) {
337                 BER_BVZERO( &a->a_nvals[j] );
338         }
339
340         /* if no values remain, delete the entire attribute */
341         if ( !a->a_numvals ) {
342                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
343                         /* Can never happen */
344                         *text = textbuf;
345                         snprintf( textbuf, textlen,
346                                 "modify/delete: %s: no such attribute",
347                                 mod->sm_desc->ad_cname.bv_val );
348                         rc = LDAP_NO_SUCH_ATTRIBUTE;
349                 }
350         } else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
351                 /* For an ordered attribute, renumber the value indices */
352                 ordered_value_sort( a, 1 );
353         }
354 return_result:
355         if ( id2 )
356                 ch_free( id2 );
357         return rc;
358 }
359
360 int
361 modify_replace_values(
362         Entry   *e,
363         Modification    *mod,
364         int             permissive,
365         const char      **text,
366         char *textbuf, size_t textlen )
367 {
368         (void) attr_delete( &e->e_attrs, mod->sm_desc );
369
370         if ( mod->sm_values ) {
371                 return modify_add_values( e, mod, permissive, text, textbuf, textlen );
372         }
373
374         return LDAP_SUCCESS;
375 }
376
377 int
378 modify_increment_values(
379         Entry   *e,
380         Modification    *mod,
381         int     permissive,
382         const char      **text,
383         char *textbuf, size_t textlen )
384 {
385         Attribute *a;
386
387         a = attr_find( e->e_attrs, mod->sm_desc );
388         if( a == NULL ) {
389                 if ( permissive ) {
390                         Modification modReplace = *mod;
391
392                         modReplace.sm_op = LDAP_MOD_REPLACE;
393
394                         return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
395                 } else {
396                         *text = textbuf;
397                         snprintf( textbuf, textlen,
398                                 "modify/increment: %s: no such attribute",
399                                 mod->sm_desc->ad_cname.bv_val );
400                         return LDAP_NO_SUCH_ATTRIBUTE;
401                 }
402         }
403
404         if ( !strcmp( a->a_desc->ad_type->sat_syntax_oid, SLAPD_INTEGER_SYNTAX )) {
405                 int i;
406                 char str[sizeof(long)*3 + 2]; /* overly long */
407                 long incr;
408
409                 if ( lutil_atol( &incr, mod->sm_values[0].bv_val ) != 0 ) {
410                         *text = "modify/increment: invalid syntax of increment";
411                         return LDAP_INVALID_SYNTAX;
412                 }
413
414                 /* treat zero and errors as a no-op */
415                 if( incr == 0 ) {
416                         return LDAP_SUCCESS;
417                 }
418
419                 for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
420                         char *tmp;
421                         long value;
422                         size_t strln;
423                         if ( lutil_atol( &value, a->a_nvals[i].bv_val ) != 0 ) {
424                                 *text = "modify/increment: invalid syntax of original value";
425                                 return LDAP_INVALID_SYNTAX;
426                         }
427                         strln = snprintf( str, sizeof(str), "%ld", value+incr );
428
429                         tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
430                         if( tmp == NULL ) {
431                                 *text = "modify/increment: reallocation error";
432                                 return LDAP_OTHER;
433                         }
434                         a->a_nvals[i].bv_val = tmp;
435                         a->a_nvals[i].bv_len = strln;
436
437                         AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
438                 }
439
440         } else {
441                 snprintf( textbuf, textlen,
442                         "modify/increment: %s: increment not supported for value syntax %s",
443                         mod->sm_desc->ad_cname.bv_val,
444                         a->a_desc->ad_type->sat_syntax_oid );
445                 return LDAP_CONSTRAINT_VIOLATION;
446         }
447
448         return LDAP_SUCCESS;
449 }
450
451 void
452 slap_mod_free(
453         Modification    *mod,
454         int                             freeit )
455 {
456         if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
457         mod->sm_values = NULL;
458
459         if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
460         mod->sm_nvalues = NULL;
461
462         if( freeit ) free( mod );
463 }
464
465 void
466 slap_mods_free(
467     Modifications       *ml,
468     int                 freevals )
469 {
470         Modifications *next;
471
472         for ( ; ml != NULL; ml = next ) {
473                 next = ml->sml_next;
474
475                 if ( freevals )
476                         slap_mod_free( &ml->sml_mod, 0 );
477                 free( ml );
478         }
479 }
480