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