]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
73aac72e39a97d8280c08299878b2ff76f8c0149
[openldap] / servers / slapd / mods.c
1 /*
2  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*
6  * Copyright (c) 1995 Regents of the University of Michigan.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms are permitted
10  * provided that this notice is preserved and that due credit is given
11  * to the University of Michigan at Ann Arbor. The name of the University
12  * may not be used to endorse or promote products derived from this
13  * software without specific prior written permission. This software
14  * is provided ``as is'' without express or implied warranty.
15  */
16
17 #include "portable.h"
18
19 #include "slap.h"
20
21 int
22 modify_check_duplicates(
23         AttributeDescription    *ad,
24         MatchingRule            *mr,
25         BerVarray               vals,
26         BerVarray               mods,
27         int                     permissive,
28         const char      **text,
29         char *textbuf, size_t textlen )
30 {
31         int             i, j, numvals = 0, nummods,
32                         rc = LDAP_SUCCESS, matched;
33 #ifdef SLAP_NVALUES
34         /* needs major reworking */
35 #else
36         BerVarray       nvals = NULL, nmods = NULL;
37
38         /*
39          * FIXME: better do the following
40          * 
41          *   - count the existing values
42          *   - count the new values
43          *   
44          *   - if the existing values are less than the new ones {
45          *       - normalize all the existing values
46          *       - for each new value {
47          *           - normalize
48          *           - check with existing
49          *           - cross-check with already normalized new vals
50          *       }
51          *   } else {
52          *       - for each new value {
53          *           - normalize
54          *           - cross-check with already normalized new vals
55          *       }
56          *       - for each existing value {
57          *           - normalize
58          *           - check with already normalized new values
59          *       }
60          *   }
61          *
62          * The first case is good when adding a lot of new values,
63          * and significantly at first import of values (e.g. adding
64          * a new group); the latter case seems to be quite important
65          * as well, because it is likely to be the most frequently
66          * used when administering the entry.  The current 
67          * implementation will always normalize all the existing
68          * values before checking.  If there's no duplicate, the
69          * performances should not change; they will in case of error.
70          */
71
72         for ( nummods = 0; mods[ nummods ].bv_val != NULL; nummods++ )
73                 /* count new values */ ;
74
75         if ( vals ) {
76                 for ( numvals = 0; vals[ numvals ].bv_val != NULL; numvals++ )
77                         /* count existing values */ ;
78
79                 if ( numvals < nummods ) {
80                         nvals = SLAP_CALLOC( numvals + 1, sizeof( struct berval ) );
81                         if( nvals == NULL ) {
82 #ifdef NEW_LOGGING
83                                 LDAP_LOG( OPERATION, ERR,
84                                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
85 #else
86                                 Debug( LDAP_DEBUG_ANY, 
87                                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
88 #endif
89                                 goto return_results;
90                         }
91
92                         /* normalize the existing values first */
93                         for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
94                                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
95                                         &vals[ j ], &nvals[ j ], text );
96
97                                 /* existing attribute values must normalize */
98                                 assert( rc == LDAP_SUCCESS );
99
100                                 if ( rc != LDAP_SUCCESS ) {
101                                         nvals[ j ].bv_val = NULL;
102                                         goto return_results;
103                                 }
104                         }
105                         nvals[ j ].bv_val = NULL;
106                 }
107         }
108
109         /*
110          * If the existing values are less than the new values,
111          * it is more convenient to normalize all the existing
112          * values and test each new value against them first,
113          * then to other already normalized values
114          */
115         nmods = SLAP_CALLOC( nummods + 1, sizeof( struct berval ) );
116         if ( nmods == NULL ) {
117 #ifdef NEW_LOGGING
118                 LDAP_LOG( OPERATION, ERR,
119                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
120 #else
121                 Debug( LDAP_DEBUG_ANY, 
122                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
123 #endif
124                 goto return_results;
125         }
126
127         for ( i=0; mods[i].bv_val != NULL; i++ ) {
128                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
129                         &mods[i], &nmods[i], text );
130
131                 if ( rc != LDAP_SUCCESS ) {
132                         nmods[i].bv_val = NULL;
133                         goto return_results;
134                 }
135
136                 if ( numvals > 0 && numvals < nummods ) {
137                         for ( matched=0, j=0; nvals[j].bv_val; j++ ) {
138                                 int match;
139
140                                 rc = (*mr->smr_match)( &match,
141                                         SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
142                                         ad->ad_type->sat_syntax,
143                                         mr, &nmods[ i ], &nvals[ j ] );
144
145                                 if ( rc != LDAP_SUCCESS ) {
146                                         nmods[ i + 1 ].bv_val = NULL;
147                                         *text = textbuf;
148                                         snprintf( textbuf, textlen,
149                                                 "%s: matching rule failed",
150                                                 ad->ad_cname.bv_val );
151                                         goto return_results;
152                                 }
153
154                                 if ( match == 0 ) {
155                                         if ( permissive ) {
156                                                 matched++;
157                                                 continue;
158                                         }
159                                         *text = textbuf;
160                                         snprintf( textbuf, textlen,
161                                                 "%s: value #%d provided more than once",
162                                                 ad->ad_cname.bv_val, i );
163                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
164                                         nmods[ i + 1 ].bv_val = NULL;
165                                         goto return_results;
166                                 }
167                         }
168
169                         if ( permissive && matched == j ) {
170                                 nmods[ i + 1 ].bv_val = NULL;
171                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
172                                 goto return_results;
173                         }
174                 }
175         
176                 for ( matched = 0, j = 0; j < i; j++ ) {
177                         int match;
178
179                         rc = (*mr->smr_match)( &match,
180                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
181                                 ad->ad_type->sat_syntax,
182                                 mr, &nmods[ i ], &nmods[ j ] );
183                         if ( rc != LDAP_SUCCESS ) {
184                                 nmods[ i + 1 ].bv_val = NULL;
185                                 *text = textbuf;
186                                 snprintf( textbuf, textlen,
187                                         "%s: matching rule failed",
188                                         ad->ad_cname.bv_val );
189                                 goto return_results;
190                         }
191
192                         if ( match == 0 ) {
193                                 if ( permissive ) {
194                                         matched++;
195                                         continue;
196                                 }
197                                 *text = textbuf;
198                                 snprintf( textbuf, textlen,
199                                         "%s: value #%d provided more than once",
200                                         ad->ad_cname.bv_val, j );
201                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
202                                 nmods[ i + 1 ].bv_val = NULL;
203                                 goto return_results;
204                         }
205                 }
206
207                 if ( permissive && matched == j ) {
208                         nmods[ i + 1 ].bv_val = NULL;
209                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
210                         goto return_results;
211                 }
212         }
213         nmods[ i ].bv_val = NULL;
214
215         /*
216          * if new values are more than existing values, it is more
217          * convenient to normalize and check all new values first,
218          * then check each new value against existing values, which 
219          * can be normalized in place
220          */
221
222         if ( numvals >= nummods ) {
223                 for ( j = 0; vals[ j ].bv_val; j++ ) {
224                         struct berval   asserted;
225
226                         rc = value_normalize( ad, SLAP_MR_EQUALITY,
227                                 &vals[ j ], &asserted, text );
228
229                         if ( rc != LDAP_SUCCESS ) {
230                                 goto return_results;
231                         }
232
233                         for ( matched = 0, i = 0; nmods[ i ].bv_val; i++ ) {
234                                 int match;
235
236                                 rc = (*mr->smr_match)( &match,
237                                         SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
238                                         ad->ad_type->sat_syntax,
239                                         mr, &nmods[ i ], &asserted );
240                                 if ( rc != LDAP_SUCCESS ) {
241                                         *text = textbuf;
242                                         snprintf( textbuf, textlen,
243                                                 "%s: matching rule failed",
244                                                 ad->ad_cname.bv_val );
245                                         goto return_results;
246                                 }
247
248                                 if ( match == 0 ) {
249                                         if ( permissive ) {
250                                                 matched++;
251                                                 continue;
252                                         }
253                                         *text = textbuf;
254                                         snprintf( textbuf, textlen,
255                                                 "%s: value #%d provided more than once",
256                                                 ad->ad_cname.bv_val, j );
257                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
258                                         goto return_results;
259                                 }
260                         }
261
262                         if ( permissive && matched == i ) {
263                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
264                                 goto return_results;
265                         }
266                 }
267         }
268
269 return_results:;
270         if ( nvals ) {
271                 ber_bvarray_free( nvals );
272         }
273         if ( nmods ) {
274                 ber_bvarray_free( nmods );
275         }
276
277 #endif
278         return rc;
279 }
280
281 int
282 modify_add_values(
283         Entry   *e,
284         Modification    *mod,
285         int     permissive,
286         const char      **text,
287         char *textbuf, size_t textlen
288 )
289 {
290         int             i, j;
291         int             matched;
292         Attribute       *a;
293         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
294         const char *op;
295
296         switch( mod->sm_op ) {
297         case LDAP_MOD_ADD:
298                 op = "add";
299                 break;
300         case LDAP_MOD_REPLACE:
301                 op = "replace";
302                 break;
303         default:
304                 op = "?";
305                 assert( 0 );
306         }
307
308         a = attr_find( e->e_attrs, mod->sm_desc );
309
310         /*
311          * With permissive set, as long as the attribute being added
312          * has the same value(s?) as the existing attribute, then the
313          * modify will succeed.
314          */
315
316         /* check if the values we're adding already exist */
317         if( mr == NULL || !mr->smr_match ) {
318 #ifdef SLAP_NVALUES
319                 /* we should have no normalized values as there is no equality rule */
320                 /* assert( mod->sm_nvalues[0].bv_val == NULL); */
321 #endif
322
323                 if ( a != NULL ) {
324                         /* do not allow add of additional attribute
325                                 if no equality rule exists */
326                         *text = textbuf;
327                         snprintf( textbuf, textlen,
328                                 "modify/%s: %s: no equality matching rule",
329                                 op, mod->sm_desc->ad_cname.bv_val );
330                         return LDAP_INAPPROPRIATE_MATCHING;
331                 }
332
333                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
334                         /* test asserted values against existing values */
335                         if( a ) {
336 #ifdef SLAP_NVALUES
337                                 /* we should have no normalized values as there
338                                         is no equality rule */
339                                 assert( a->a_nvals == NULL);
340 #endif
341                                 for( matched = 0, j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
342                                         if ( bvmatch( &mod->sm_bvalues[i], &a->a_vals[j] ) ) {
343                                                 if ( permissive ) {
344                                                         matched++;
345                                                         continue;
346                                                 }
347                                                 /* value exists already */
348                                                 *text = textbuf;
349                                                 snprintf( textbuf, textlen,
350                                                         "modify/%s: %s: value #%i already exists",
351                                                         op, mod->sm_desc->ad_cname.bv_val, j );
352                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
353                                         }
354                                 }
355                                 if ( permissive && matched == j ) {
356                                         /* values already exist; do nothing */
357                                         return LDAP_SUCCESS;
358                                 }
359                         }
360
361                         /* test asserted values against themselves */
362                         for( j = 0; j < i; j++ ) {
363                                 if ( bvmatch( &mod->sm_bvalues[i],
364                                         &mod->sm_bvalues[j] ) ) {
365
366                                         /* value exists already */
367                                         *text = textbuf;
368                                         snprintf( textbuf, textlen,
369                                                 "modify/%s: %s: value #%i already exists",
370                                                 op, mod->sm_desc->ad_cname.bv_val, j );
371                                         return LDAP_TYPE_OR_VALUE_EXISTS;
372                                 }
373                         }
374                 }
375
376         } else {
377 #ifdef SLAP_NVALUES
378                 /* no normalization is done in this routine nor
379                  * in the matching routines called by this routine. 
380                  * values are now normalized once on input to the
381                  * server (whether from LDAP or from the underlying
382                  * database).
383                  * This should outperform the old code.  No numbers
384                  * are available yet.
385                  */
386 #else
387                 /*
388                  * The original code performs ( n ) normalizations 
389                  * and ( n * ( n - 1 ) / 2 ) matches, which hide
390                  * the same number of normalizations.  The new code
391                  * performs the same number of normalizations ( n )
392                  * and ( n * ( n - 1 ) / 2 ) mem compares, far less
393                  * expensive than an entire match, if a match is
394                  * equivalent to a normalization and a mem compare ...
395                  * 
396                  * This is far more memory expensive than the previous,
397                  * but it can heavily improve performances when big
398                  * chunks of data are added (typical example is a group
399                  * with thousands of DN-syntax members; on my system:
400                  * for members of 5-RDN DNs,
401
402                 members         orig            bvmatch (dirty) new
403                 1000            0m38.456s       0m0.553s        0m0.608s
404                 2000            2m33.341s       0m0.851s        0m1.003s
405
406                  * Moreover, 100 groups with 10000 members each were
407                  * added in 37m27.933s (an analogous LDIF file was
408                  * loaded into Active Directory in 38m28.682s, BTW).
409                  * 
410                  * Maybe we could switch to the new algorithm when
411                  * the number of values overcomes a given threshold?
412                  */
413 #endif
414
415                 int             rc;
416
417                 if ( mod->sm_bvalues[1].bv_val == 0 ) {
418                         if ( a != NULL ) {
419                                 int             i;
420 #ifndef SLAP_NVALUES
421                                 struct berval   asserted;
422                                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
423                                         &mod->sm_bvalues[ 0 ], &asserted, text );
424                                 if ( rc != LDAP_SUCCESS ) {
425                                         return rc;
426                                 }
427 #endif
428
429                                 for ( matched = 0, i = 0; a->a_vals[ i ].bv_val; i++ ) {
430                                         int     match;
431
432 #ifdef SLAP_NVALUES
433                                         if( mod->sm_nvalues ) {
434                                                 rc = value_match( &match, mod->sm_desc, mr,
435                                                         SLAP_MR_EQUALITY
436                                                                 | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
437                                                                 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
438                                                                 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
439                                                         &a->a_nvals[i],
440                                                         &mod->sm_nvalues[0],
441                                                         text );
442
443                                         } else {
444                                                 rc = value_match( &match, mod->sm_desc, mr,
445                                                         SLAP_MR_EQUALITY
446                                                                 | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
447                                                         &a->a_vals[i],
448                                                         &mod->sm_values[0],
449                                                         text );
450                                         }
451
452 #else
453                                         rc = value_match( &match, mod->sm_desc, mr,
454                                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
455                                                 &a->a_vals[i],
456                                                 &asserted,
457                                                 text );
458 #endif
459
460                                         if( rc == LDAP_SUCCESS && match == 0 ) {
461                                                 if ( permissive ) {
462                                                         matched++;
463                                                         continue;
464                                                 }
465 #ifndef SLAP_NVALUES
466                                                 free( asserted.bv_val );
467 #endif
468                                                 *text = textbuf;
469                                                 snprintf( textbuf, textlen,
470                                                         "modify/%s: %s: value #0 already exists",
471                                                         op, mod->sm_desc->ad_cname.bv_val, 0 );
472                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
473                                         }
474                                 }
475 #ifndef SLAP_NVALUES
476                                 free( asserted.bv_val );
477 #endif
478                                 if ( permissive && matched == i ) {
479                                         /* values already exist; do nothing */
480                                         return LDAP_SUCCESS;
481                                 }
482                         }
483
484                 } else {
485                         rc = modify_check_duplicates( mod->sm_desc, mr,
486                                         a ? a->a_vals : NULL, mod->sm_bvalues,
487                                         permissive,
488                                         text, textbuf, textlen );
489
490                         if ( permissive && rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
491                                 return LDAP_SUCCESS;
492                         }
493
494                         if ( rc != LDAP_SUCCESS ) {
495                                 return rc;
496                         }
497                 }
498         }
499
500         /* no - add them */
501 #ifdef SLAP_NVALUES
502         if( attr_merge( e, mod->sm_desc, mod->sm_values, mod->sm_nvalues ) != 0 )
503 #else
504         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 )
505 #endif
506         {
507                 /* this should return result of attr_merge */
508                 *text = textbuf;
509                 snprintf( textbuf, textlen,
510                         "modify/%s: %s: merge error",
511                         op, mod->sm_desc->ad_cname.bv_val );
512                 return LDAP_OTHER;
513         }
514
515         return LDAP_SUCCESS;
516 }
517
518 int
519 modify_delete_values(
520         Entry   *e,
521         Modification    *mod,
522         int     permissive,
523         const char      **text,
524         char *textbuf, size_t textlen
525 )
526 {
527         int             i, j, k, rc = LDAP_SUCCESS;
528         Attribute       *a;
529         MatchingRule    *mr = mod->sm_desc->ad_type->sat_equality;
530 #ifndef SLAP_NVALUES
531         BerVarray       nvals = NULL;
532 #endif
533         char            dummy = '\0';
534
535         /*
536          * If permissive is set, then the non-existence of an 
537          * attribute is not treated as an error.
538          */
539
540         /* delete the entire attribute */
541         if ( mod->sm_bvalues == NULL ) {
542                 rc = attr_delete( &e->e_attrs, mod->sm_desc );
543
544                 if( permissive ) {
545                         rc = LDAP_SUCCESS;
546                 } else if( rc != LDAP_SUCCESS ) {
547                         *text = textbuf;
548                         snprintf( textbuf, textlen,
549                                 "modify/delete: %s: no such attribute",
550                                 mod->sm_desc->ad_cname.bv_val );
551                         rc = LDAP_NO_SUCH_ATTRIBUTE;
552                 }
553                 return rc;
554         }
555
556         if( mr == NULL || !mr->smr_match ) {
557                 /* disallow specific attributes from being deleted if
558                         no equality rule */
559                 *text = textbuf;
560                 snprintf( textbuf, textlen,
561                         "modify/delete: %s: no equality matching rule",
562                         mod->sm_desc->ad_cname.bv_val );
563                 return LDAP_INAPPROPRIATE_MATCHING;
564         }
565
566         /* delete specific values - find the attribute first */
567         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
568                 if( permissive ) {
569                         return LDAP_SUCCESS;
570                 }
571                 *text = textbuf;
572                 snprintf( textbuf, textlen,
573                         "modify/delete: %s: no such attribute",
574                         mod->sm_desc->ad_cname.bv_val );
575                 return LDAP_NO_SUCH_ATTRIBUTE;
576         }
577
578 #ifndef SLAP_NVALUES
579         /* find each value to delete */
580         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ )
581                 /* count existing values */ ;
582
583         nvals = (BerVarray)SLAP_CALLOC( j + 1, sizeof ( struct berval ) );
584         if( nvals == NULL ) {
585 #ifdef NEW_LOGGING
586                 LDAP_LOG( OPERATION, ERR,
587                         "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
588 #else
589                 Debug( LDAP_DEBUG_ANY, 
590                         "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
591 #endif
592                                 goto return_results;
593         }
594
595         /* normalize existing values */
596         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ ) {
597                 rc = value_normalize( a->a_desc, SLAP_MR_EQUALITY,
598                         &a->a_vals[ j ], &nvals[ j ], text );
599
600                 if ( rc != LDAP_SUCCESS ) {
601                         nvals[ j ].bv_val = NULL;
602                         goto return_results;
603                 }
604         }
605 #endif
606
607         for ( i = 0; mod->sm_values[i].bv_val != NULL; i++ ) {
608                 int     found = 0;
609 #ifndef SLAP_NVALUES
610                 struct  berval asserted;
611
612                 /* normalize the value to be deleted */
613                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
614                         &mod->sm_bvalues[ i ], &asserted, text );
615
616                 if( rc != LDAP_SUCCESS ) {
617                         goto return_results;
618                 }
619
620                 /* search it */
621                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ )
622 #else
623                 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ )
624 #endif
625                 {
626                         int match;
627
628 #ifndef SLAP_NVALUES
629                         if ( nvals[j].bv_val == &dummy ) {
630                                 continue;
631                         }
632
633 #endif
634 #ifdef SLAP_NVALUES
635                         if( mod->sm_nvalues ) {
636                                 assert( a->a_nvals );
637                                 rc = (*mr->smr_match)( &match,
638                                         SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
639                                                 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
640                                                 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
641                                         a->a_desc->ad_type->sat_syntax,
642                                         mr, &a->a_nvals[j],
643                                         &mod->sm_nvalues[i] );
644                         } else {
645                                 assert( a->a_nvals == NULL );
646                                 rc = (*mr->smr_match)( &match,
647                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
648                                         a->a_desc->ad_type->sat_syntax,
649                                         mr, &a->a_nvals[j],
650                                         &mod->sm_values[i] );
651                         }
652 #else
653                         rc = (*mr->smr_match)( &match,
654                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
655                                 a->a_desc->ad_type->sat_syntax,
656                                 mr, &nvals[ j ],
657                                 &asserted );
658 #endif
659
660                         if ( rc != LDAP_SUCCESS ) {
661 #ifndef SLAP_NVALUES
662                                 free( asserted.bv_val );
663 #endif
664                                 *text = textbuf;
665                                 snprintf( textbuf, textlen,
666                                         "%s: matching rule failed",
667                                         mod->sm_desc->ad_cname.bv_val );
668                                 goto return_results;
669                         }
670
671                         if ( match != 0 ) {
672                                 continue;
673                         }
674
675                         found = 1;
676
677                         /* delete value and mark it as dummy */
678 #ifdef SLAP_NVALUES
679                         free( a->a_vals[j].bv_val );
680                         a->a_vals[j].bv_val = &dummy;
681                         if( a->a_nvals ) {
682                                 free( a->a_nvals[j].bv_val );
683                                 a->a_nvals[j].bv_val = &dummy;
684                         }
685 #else
686                         free( nvals[ j ].bv_val );
687                         nvals[ j ].bv_val = &dummy;
688 #endif
689
690                         break;
691                 }
692
693 #ifndef SLAP_NVALUES
694                 free( asserted.bv_val );
695 #endif
696
697                 if ( found == 0 ) {
698                         *text = textbuf;
699                         snprintf( textbuf, textlen,
700                                 "modify/delete: %s: no such value",
701                                 mod->sm_desc->ad_cname.bv_val );
702                         rc = LDAP_NO_SUCH_ATTRIBUTE;
703                         goto return_results;
704                 }
705         }
706
707         /* compact array skipping dummies */
708 #ifdef SLAP_NVALUES
709         for ( k = 0, j = 0; a->a_vals[k].bv_val != NULL; k++ )
710 #else
711         for ( k = 0, j = 0; nvals[k].bv_val != NULL; j++, k++ )
712 #endif
713         {
714 #ifdef SLAP_NVALUES
715                 /* skip dummies */
716                 if( a->a_vals[k].bv_val == &dummy ) {
717                         assert( a->a_nvals == NULL || a->a_nvals[k].bv_val == &dummy );
718                         continue;
719                 }
720 #else
721                 /* delete and skip dummies */ ;
722                 for ( ; nvals[ k ].bv_val == &dummy; k++ ) {
723                         free( a->a_vals[ k ].bv_val );
724                 }
725 #endif
726                 if ( j != k ) {
727                         a->a_vals[ j ] = a->a_vals[ k ];
728 #ifdef SLAP_NVALUES
729                         if (a->a_nvals) {
730                                 a->a_nvals[ j ] = a->a_nvals[ k ];
731                         }
732 #endif
733                 }
734
735 #ifndef SLAP_NVALUES
736                 if ( a->a_vals[ k ].bv_val == NULL ) {
737                         break;
738                 }
739 #else
740                 j++;
741 #endif
742         }
743
744         a->a_vals[j].bv_val = NULL;
745 #ifdef SLAP_NVALUES
746         if (a->a_nvals) a->a_nvals[j].bv_val = NULL;
747 #else
748
749         assert( i == k - j );
750 #endif
751
752         /* if no values remain, delete the entire attribute */
753         if ( a->a_vals[0].bv_val == NULL ) {
754                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
755                         *text = textbuf;
756                         snprintf( textbuf, textlen,
757                                 "modify/delete: %s: no such attribute",
758                                 mod->sm_desc->ad_cname.bv_val );
759                         rc = LDAP_NO_SUCH_ATTRIBUTE;
760                 }
761         }
762
763 return_results:;
764 #ifndef SLAP_NVALUES
765         if ( nvals ) {
766                 /* delete the remaining normalized values */
767                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
768                         if ( nvals[ j ].bv_val != &dummy ) {
769                                 ber_memfree( nvals[ j ].bv_val );
770                         }
771                 }
772                 ber_memfree( nvals );
773         }
774 #endif
775
776         return rc;
777 }
778
779 int
780 modify_replace_values(
781         Entry   *e,
782         Modification    *mod,
783         int             permissive,
784         const char      **text,
785         char *textbuf, size_t textlen
786 )
787 {
788         (void) attr_delete( &e->e_attrs, mod->sm_desc );
789
790         if ( mod->sm_bvalues ) {
791                 return modify_add_values( e, mod, permissive, text, textbuf, textlen );
792         }
793
794         return LDAP_SUCCESS;
795 }
796
797 void
798 slap_mod_free(
799         Modification    *mod,
800         int                             freeit
801 )
802 {
803         if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
804         mod->sm_values = NULL;
805
806 #ifdef SLAP_NVALUES
807         if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
808         mod->sm_nvalues = NULL;
809 #endif
810
811         if( freeit ) free( mod );
812 }
813
814 void
815 slap_mods_free(
816     Modifications       *ml
817 )
818 {
819         Modifications *next;
820
821         for ( ; ml != NULL; ml = next ) {
822                 next = ml->sml_next;
823
824                 slap_mod_free( &ml->sml_mod, 0 );
825                 free( ml );
826         }
827 }
828