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