]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
Cleanup up LDAP_CLIENT_UPDATE code... including some bug fixing.
[openldap] / servers / slapd / mods.c
1 /*
2  * Copyright 1998-2002 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         const char      **text,
28         char *textbuf, size_t textlen )
29 {
30         int             i, j, numvals = 0, nummods,
31                         rc = LDAP_SUCCESS;
32         BerVarray       nvals = NULL, nmods;
33
34         /*
35          * FIXME: better do the following
36          * 
37          *   - count the existing values
38          *   - count the new values
39          *   
40          *   - if the existing values are less than the new ones {
41          *       - normalize all the existing values
42          *       - for each new value {
43          *           - normalize
44          *           - check with existing
45          *           - cross-check with already normalized new vals
46          *       }
47          *   } else {
48          *       - for each new value {
49          *           - normalize
50          *           - cross-check with already normalized new vals
51          *       }
52          *       - for each existing value {
53          *           - normalize
54          *           - check with already normalized new values
55          *       }
56          *   }
57          *
58          * The first case is good when adding a lot of new values,
59          * and significantly at first import of values (e.g. adding
60          * a new group); the latter case seems to be quite important
61          * as well, because it is likely to be the most frequently
62          * used when administering the entry.  The current 
63          * implementation will always normalize all the existing
64          * values before checking.  If there's no duplicate, the
65          * performances should not change; they will in case of error.
66          */
67
68         for ( nummods = 0; mods[ nummods ].bv_val != NULL; nummods++ )
69                 /* count new values */ ;
70
71         if ( vals ) {
72                 for ( numvals = 0; vals[ numvals ].bv_val != NULL; numvals++ )
73                         /* count existing values */ ;
74
75                 if ( numvals < nummods ) {
76                         nvals = ch_calloc( numvals + 1, sizeof( struct berval ) );
77
78                         /* normalize the existing values first */
79                         for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
80                                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
81                                         &vals[ j ], &nvals[ j ], text );
82
83                                 /* existing attribute values must normalize */
84                                 assert( rc == LDAP_SUCCESS );
85
86                                 if ( rc != LDAP_SUCCESS ) {
87                                         nvals[ j ].bv_val = NULL;
88                                         goto return_results;
89                                 }
90                         }
91                         nvals[ j ].bv_val = NULL;
92                 }
93         }
94
95         /*
96          * If the existing values are less than the new values,
97          * it is more convenient to normalize all the existing
98          * values and test each new value against them first,
99          * then to other already normalized values
100          */
101         nmods = ch_calloc( nummods + 1, sizeof( struct berval ) );
102
103         for ( i = 0; mods[ i ].bv_val != NULL; i++ ) {
104                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
105                         &mods[ i ], &nmods[ i ], text );
106
107                 if ( rc != LDAP_SUCCESS ) {
108                         nmods[ i ].bv_val = NULL;
109                         goto return_results;
110                 }
111
112                 if ( numvals > 0 && numvals < nummods ) {
113                         for ( j = 0; nvals[ j ].bv_val; j++ ) {
114                                 int match;
115
116                                 rc = (*mr->smr_match)( &match,
117                                         SLAP_MR_VALUE_SYNTAX_MATCH,
118                                         ad->ad_type->sat_syntax,
119                                         mr, &nmods[ i ], &nvals[ j ] );
120                                 if ( rc != LDAP_SUCCESS ) {
121                                         nmods[ i + 1 ].bv_val = NULL;
122                                         goto return_results;
123                                 }
124         
125                                 if ( match == 0 ) {
126                                         snprintf( textbuf, textlen,
127                                                 "%s: value #%d provided more than once",
128                                                 ad->ad_cname.bv_val, i );
129                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
130                                         nmods[ i + 1 ].bv_val = NULL;
131                                         goto return_results;
132                                 }
133                         }
134                 }
135         
136                 for ( j = 0; j < i; j++ ) {
137                         int match;
138
139                         rc = (*mr->smr_match)( &match,
140                                 SLAP_MR_VALUE_SYNTAX_MATCH,
141                                 ad->ad_type->sat_syntax,
142                                 mr, &nmods[ i ], &nmods[ j ] );
143                         if ( rc != LDAP_SUCCESS ) {
144                                 nmods[ i + 1 ].bv_val = NULL;
145                                 goto return_results;
146                         }
147
148                         if ( match == 0 ) {
149                                 snprintf( textbuf, textlen,
150                                         "%s: value #%d provided more than once",
151                                         ad->ad_cname.bv_val, j );
152                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
153                                 nmods[ i + 1 ].bv_val = NULL;
154                                 goto return_results;
155                         }
156                 }
157         }
158         nmods[ i ].bv_val = NULL;
159
160         /*
161          * if new values are more than existing values, it is more
162          * convenient to normalize and check all new values first,
163          * then check each new value against existing values, which 
164          * can be normalized in place
165          */
166
167         if ( numvals >= nummods ) {
168                 for ( j = 0; vals[ j ].bv_val; j++ ) {
169                         struct berval   asserted;
170
171                         rc = value_normalize( ad, SLAP_MR_EQUALITY,
172                                 &vals[ j ], &asserted, text );
173
174                         if ( rc != LDAP_SUCCESS ) {
175                                 goto return_results;
176                         }
177
178                         for ( i = 0; nmods[ i ].bv_val; i++ ) {
179                                 int match;
180
181                                 rc = (*mr->smr_match)( &match,
182                                         SLAP_MR_VALUE_SYNTAX_MATCH,
183                                         ad->ad_type->sat_syntax,
184                                         mr, &nmods[ i ], &asserted );
185                                 if ( rc != LDAP_SUCCESS ) {
186                                         goto return_results;
187                                 }
188
189                                 if ( match == 0 ) {
190                                         snprintf( textbuf, textlen,
191                                                 "%s: value #%d provided more than once",
192                                                 ad->ad_cname.bv_val, j );
193                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
194                                         goto return_results;
195                                 }
196                         }
197
198                 }
199         }
200
201 return_results:;
202         if ( nvals ) {
203                 ber_bvarray_free( nvals );
204         }
205         if ( nmods ) {
206                 ber_bvarray_free( nmods );
207         }
208
209         return rc;
210 }
211
212 int
213 modify_add_values(
214         Entry   *e,
215         Modification    *mod,
216         const char      **text,
217         char *textbuf, size_t textlen
218 )
219 {
220         int             i, j;
221         Attribute       *a;
222         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
223         const char *op;
224
225         switch( mod->sm_op ) {
226         case LDAP_MOD_ADD:
227                 op = "add";
228                 break;
229         case LDAP_MOD_REPLACE:
230                 op = "replace";
231                 break;
232         default:
233                 op = "?";
234                 assert( 0 );
235         }
236
237         a = attr_find( e->e_attrs, mod->sm_desc );
238
239         /* check if the values we're adding already exist */
240         if( mr == NULL || !mr->smr_match ) {
241                 if ( a != NULL ) {
242                         /* do not allow add of additional attribute
243                                 if no equality rule exists */
244                         *text = textbuf;
245                         snprintf( textbuf, textlen,
246                                 "modify/%s: %s: no equality matching rule",
247                                 op, mod->sm_desc->ad_cname.bv_val );
248                         return LDAP_INAPPROPRIATE_MATCHING;
249                 }
250
251                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
252                         /* test asserted values against existing values */
253                         if( a ) {
254                                 for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
255                                         if ( bvmatch( &mod->sm_bvalues[i],
256                                                 &a->a_vals[j] ) ) {
257
258                                                 /* value exists already */
259                                                 *text = textbuf;
260                                                 snprintf( textbuf, textlen,
261                                                         "modify/%s: %s: value #%i already exists",
262                                                         op, mod->sm_desc->ad_cname.bv_val, j );
263                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
264                                         }
265                                 }
266                         }
267
268                         /* test asserted values against themselves */
269                         for( j = 0; j < i; j++ ) {
270                                 if ( bvmatch( &mod->sm_bvalues[i],
271                                         &mod->sm_bvalues[j] ) ) {
272
273                                         /* value exists already */
274                                         *text = textbuf;
275                                         snprintf( textbuf, textlen,
276                                                 "modify/%s: %s: value #%i already exists",
277                                                 op, mod->sm_desc->ad_cname.bv_val, j );
278                                         return LDAP_TYPE_OR_VALUE_EXISTS;
279                                 }
280                         }
281                 }
282
283         } else {
284
285                 /*
286                  * The original code performs ( n ) normalizations 
287                  * and ( n * ( n - 1 ) / 2 ) matches, which hide
288                  * the same number of normalizations.  The new code
289                  * performs the same number of normalizations ( n )
290                  * and ( n * ( n - 1 ) / 2 ) mem compares, far less
291                  * expensive than an entire match, if a match is
292                  * equivalent to a normalization and a mem compare ...
293                  * 
294                  * This is far more memory expensive than the previous,
295                  * but it can heavily improve performances when big
296                  * chunks of data are added (typical example is a group
297                  * with thousands of DN-syntax members; on my system:
298                  * for members of 5-RDN DNs,
299
300                 members         orig            bvmatch (dirty) new
301                 1000            0m38.456s       0m0.553s        0m0.608s
302                 2000            2m33.341s       0m0.851s        0m1.003s
303
304                  * Moreover, 100 groups with 10000 members each were
305                  * added in 37m27.933s (an analogous LDIF file was
306                  * loaded into Active Directory in 38m28.682s, BTW).
307                  * 
308                  * Maybe we could switch to the new algorithm when
309                  * the number of values overcomes a given threshold?
310                  */
311
312                 int             rc;
313                 const char      *text = NULL;
314                 char            textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
315
316                 if ( mod->sm_bvalues[ 1 ].bv_val == 0 ) {
317                         if ( a != NULL ) {
318                                 struct berval   asserted;
319                                 int             i;
320
321                                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
322                                         &mod->sm_bvalues[ 0 ], &asserted, &text );
323
324                                 if ( rc != LDAP_SUCCESS ) {
325                                         return rc;
326                                 }
327
328                                 for ( i = 0; a->a_vals[ i ].bv_val; i++ ) {
329                                         int     match;
330
331                                         rc = value_match( &match, mod->sm_desc, mr,
332                                                 SLAP_MR_VALUE_SYNTAX_MATCH,
333                                                 &a->a_vals[ i ], &asserted, &text );
334
335                                         if( rc == LDAP_SUCCESS && match == 0 ) {
336                                                 free( asserted.bv_val );
337                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
338                                         }
339                                 }
340                         }
341
342                 } else {
343                         rc = modify_check_duplicates( mod->sm_desc, mr,
344                                         a ? a->a_vals : NULL, mod->sm_bvalues,
345                                         &text, textbuf, sizeof( textbuf ) );
346         
347                         if ( rc != LDAP_SUCCESS ) {
348                                 return rc;
349                         }
350                 }
351         }
352
353         /* no - add them */
354         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
355                 /* this should return result of attr_merge */
356                 *text = textbuf;
357                 snprintf( textbuf, textlen,
358                         "modify/%s: %s: merge error",
359                         op, mod->sm_desc->ad_cname.bv_val );
360                 return LDAP_OTHER;
361         }
362
363         return LDAP_SUCCESS;
364 }
365
366 int
367 modify_delete_values(
368         Entry   *e,
369         Modification    *mod,
370         const char      **text,
371         char *textbuf, size_t textlen
372 )
373 {
374         int             i, j, k, rc = LDAP_SUCCESS;
375         Attribute       *a;
376         MatchingRule    *mr = mod->sm_desc->ad_type->sat_equality;
377         BerVarray       nvals = NULL;
378         char            dummy = '\0';
379
380         /* delete the entire attribute */
381         if ( mod->sm_bvalues == NULL ) {
382                 rc = attr_delete( &e->e_attrs, mod->sm_desc );
383
384                 if( rc != LDAP_SUCCESS ) {
385                         *text = textbuf;
386                         snprintf( textbuf, textlen,
387                                 "modify/delete: %s: no such attribute",
388                                 mod->sm_desc->ad_cname.bv_val );
389                         rc = LDAP_NO_SUCH_ATTRIBUTE;
390                 }
391                 return rc;
392         }
393
394         if( mr == NULL || !mr->smr_match ) {
395                 /* disallow specific attributes from being deleted if
396                         no equality rule */
397                 *text = textbuf;
398                 snprintf( textbuf, textlen,
399                         "modify/delete: %s: no equality matching rule",
400                         mod->sm_desc->ad_cname.bv_val );
401                 return LDAP_INAPPROPRIATE_MATCHING;
402         }
403
404         /* delete specific values - find the attribute first */
405         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
406                 *text = textbuf;
407                 snprintf( textbuf, textlen,
408                         "modify/delete: %s: no such attribute",
409                         mod->sm_desc->ad_cname.bv_val );
410                 return LDAP_NO_SUCH_ATTRIBUTE;
411         }
412
413         /* find each value to delete */
414         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ )
415                 /* count existing values */ ;
416
417         nvals = (BerVarray)ch_calloc( j + 1, sizeof ( struct berval ) );
418
419         /* normalize existing values */
420         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ ) {
421                 rc = value_normalize( a->a_desc, SLAP_MR_EQUALITY,
422                         &a->a_vals[ j ], &nvals[ j ], text );
423
424                 if ( rc != LDAP_SUCCESS ) {
425                         nvals[ j ].bv_val = NULL;
426                         goto return_results;
427                 }
428         }
429
430         for ( i = 0; mod->sm_bvalues[ i ].bv_val != NULL; i++ ) {
431                 struct  berval asserted;
432                 int     found = 0;
433
434                 /* normalize the value to be deleted */
435                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
436                         &mod->sm_bvalues[ i ], &asserted, text );
437
438                 if( rc != LDAP_SUCCESS ) {
439                         goto return_results;
440                 }
441
442                 /* search it */
443                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
444                         int match;
445
446                         if ( nvals[ j ].bv_val == &dummy ) {
447                                 continue;
448                         }
449
450                         rc = (*mr->smr_match)( &match,
451                                 SLAP_MR_VALUE_SYNTAX_MATCH,
452                                 a->a_desc->ad_type->sat_syntax,
453                                 mr, &nvals[ j ], &asserted );
454
455                         if ( rc != LDAP_SUCCESS ) {
456                                 free( asserted.bv_val );
457                                 goto return_results;
458                         }
459
460                         if ( match != 0 ) {
461                                 continue;
462                         }
463
464                         found = 1;
465
466                         /* delete value and mark it as dummy */
467                         free( nvals[ j ].bv_val );
468                         nvals[ j ].bv_val = &dummy;
469
470                         break;
471                 }
472
473                 free( asserted.bv_val );
474
475                 if ( found == 0 ) {
476                         *text = textbuf;
477                         snprintf( textbuf, textlen,
478                                 "modify/delete: %s: no such value",
479                                 mod->sm_desc->ad_cname.bv_val );
480                         rc = LDAP_NO_SUCH_ATTRIBUTE;
481                         goto return_results;
482                 }
483         }
484
485         /* compact array skipping dummies */
486         for ( k = 0, j = 0; nvals[ k ].bv_val != NULL; j++, k++ ) {
487
488                 /* delete and skip dummies */ ;
489                 for ( ; nvals[ k ].bv_val == &dummy; k++ ) {
490                         free( a->a_vals[ k ].bv_val );
491                 }
492
493                 if ( j != k ) {
494                         a->a_vals[ j ] = a->a_vals[ k ];
495                 }
496
497                 if ( a->a_vals[ k ].bv_val == NULL ) {
498                         break;
499                 }
500         }
501         a->a_vals[ j ].bv_val = NULL;
502
503         assert( i == k - j );
504
505         /* if no values remain, delete the entire attribute */
506         if ( a->a_vals[0].bv_val == NULL ) {
507                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
508                         *text = textbuf;
509                         snprintf( textbuf, textlen,
510                                 "modify/delete: %s: no such attribute",
511                                 mod->sm_desc->ad_cname.bv_val );
512                         rc = LDAP_NO_SUCH_ATTRIBUTE;
513                 }
514         }
515
516 return_results:;
517         if ( nvals ) {
518                 /* delete the remaining normalized values */
519                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
520                         if ( nvals[ j ].bv_val != &dummy ) {
521                                 ber_memfree( nvals[ j ].bv_val );
522                         }
523                 }
524                 ber_memfree( nvals );
525         }
526
527         return rc;
528 }
529
530 int
531 modify_replace_values(
532         Entry   *e,
533         Modification    *mod,
534         const char      **text,
535         char *textbuf, size_t textlen
536 )
537 {
538         (void) attr_delete( &e->e_attrs, mod->sm_desc );
539
540         if ( mod->sm_bvalues ) {
541                 return modify_add_values( e, mod, text, textbuf, textlen );
542         }
543
544         return LDAP_SUCCESS;
545 }
546
547 void
548 slap_mod_free(
549         Modification    *mod,
550         int                             freeit
551 )
552 {
553 #if 0
554         if ( mod->sm_type.bv_val)
555                 free( mod->sm_type.bv_val );
556 #endif
557         if ( mod->sm_bvalues != NULL )
558                 ber_bvarray_free( mod->sm_bvalues );
559
560         if( freeit )
561                 free( mod );
562 }
563
564 void
565 slap_mods_free(
566     Modifications       *ml
567 )
568 {
569         Modifications *next;
570
571         for ( ; ml != NULL; ml = next ) {
572                 next = ml->sml_next;
573
574                 slap_mod_free( &ml->sml_mod, 0 );
575                 free( ml );
576         }
577 }
578