]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/map.c
a76c5f0f856f92ddc1418dbaa69fd2937966ff12
[openldap] / servers / slapd / back-meta / map.c
1 /* map.c - ldap backend mapping routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2003 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by the Howard Chu for inclusion
18  * in OpenLDAP Software and subsequently enhanced by Pierangelo
19  * Masarati.
20  */
21 /* This is an altered version */
22 /*
23  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
24  * 
25  * Permission is granted to anyone to use this software for any purpose
26  * on any computer system, and to alter it and redistribute it, subject
27  * to the following restrictions:
28  * 
29  * 1. The author is not responsible for the consequences of use of this
30  *    software, no matter how awful, even if they arise from flaws in it.
31  * 
32  * 2. The origin of this software must not be misrepresented, either by
33  *    explicit claim or by omission.  Since few users ever read sources,
34  *    credits should appear in the documentation.
35  * 
36  * 3. Altered versions must be plainly marked as such, and must not be
37  *    misrepresented as being the original software.  Since few users
38  *    ever read sources, credits should appear in the documentation.
39  * 
40  * 4. This notice may not be removed or altered.
41  *
42  *
43  *
44  * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
45  * 
46  * This software is being modified by Pierangelo Masarati.
47  * The previously reported conditions apply to the modified code as well.
48  * Changes in the original code are highlighted where required.
49  * Credits for the original code go to the author, Howard Chu.
50  */
51
52 #include "portable.h"
53
54 #include <stdio.h>
55
56 #include <ac/string.h>
57 #include <ac/socket.h>
58
59 #include "slap.h"
60 #include "../back-ldap/back-ldap.h"
61 #include "back-meta.h"
62
63 #undef ldap_debug       /* silence a warning in ldap-int.h */
64 #include "../../../libraries/libldap/ldap-int.h"
65
66 int
67 mapping_cmp ( const void *c1, const void *c2 )
68 {
69         struct ldapmapping *map1 = (struct ldapmapping *)c1;
70         struct ldapmapping *map2 = (struct ldapmapping *)c2;
71         int rc = map1->src.bv_len - map2->src.bv_len;
72         if (rc) return rc;
73         return ( strcasecmp(map1->src.bv_val, map2->src.bv_val) );
74 }
75
76 int
77 mapping_dup ( void *c1, void *c2 )
78 {
79         struct ldapmapping *map1 = (struct ldapmapping *)c1;
80         struct ldapmapping *map2 = (struct ldapmapping *)c2;
81
82         return( ( strcasecmp(map1->src.bv_val, map2->src.bv_val) == 0 ) ? -1 : 0 );
83 }
84
85 void
86 ldap_back_map_init ( struct ldapmap *lm, struct ldapmapping **m )
87 {
88         struct ldapmapping *mapping;
89
90         assert( m );
91
92         *m = NULL;
93         
94         mapping = (struct ldapmapping *)ch_calloc( 2, 
95                         sizeof( struct ldapmapping ) );
96         if ( mapping == NULL ) {
97                 return;
98         }
99
100         ber_str2bv( "objectclass", sizeof("objectclass")-1, 1, &mapping->src);
101         ber_dupbv( &mapping->dst, &mapping->src );
102         mapping[1].src = mapping->src;
103         mapping[1].dst = mapping->dst;
104
105         avl_insert( &lm->map, (caddr_t)mapping, 
106                         mapping_cmp, mapping_dup );
107         avl_insert( &lm->remap, (caddr_t)&mapping[1], 
108                         mapping_cmp, mapping_dup );
109         *m = mapping;
110 }
111
112 void
113 ldap_back_map ( struct ldapmap *map, struct berval *s, struct berval *bv,
114         int remap )
115 {
116         Avlnode *tree;
117         struct ldapmapping *mapping, fmapping;
118
119         if (remap == BACKLDAP_REMAP)
120                 tree = map->remap;
121         else
122                 tree = map->map;
123
124         bv->bv_len = 0;
125         bv->bv_val = NULL;
126         fmapping.src = *s;
127         mapping = (struct ldapmapping *)avl_find( tree, (caddr_t)&fmapping, mapping_cmp );
128         if (mapping != NULL) {
129                 if ( mapping->dst.bv_val )
130                         *bv = mapping->dst;
131                 return;
132         }
133
134         if (!map->drop_missing)
135                 *bv = *s;
136
137         return;
138 }
139
140 int
141 ldap_back_map_attrs(
142                 struct ldapmap *at_map,
143                 AttributeName *an,
144                 int remap,
145                 char ***mapped_attrs
146 )
147 {
148         int i, j;
149         char **na;
150         struct berval mapped;
151
152         if (an == NULL) {
153                 *mapped_attrs = NULL;
154                 return LDAP_SUCCESS;
155         }
156
157         for (i = 0; an[i].an_name.bv_val; i++) {
158                 /*  */
159         }
160
161         na = (char **)ch_calloc( i + 1, sizeof(char *) );
162         if (na == NULL) {
163                 *mapped_attrs = NULL;
164                 return LDAP_NO_MEMORY;
165         }
166
167         for (i = j = 0; an[i].an_name.bv_val; i++) {
168                 ldap_back_map(at_map, &an[i].an_name, &mapped, remap);
169                 if (mapped.bv_val != NULL && mapped.bv_val != '\0')
170                         na[j++] = mapped.bv_val;
171         }
172         if (j == 0 && i != 0)
173                 na[j++] = LDAP_NO_ATTRS;
174         na[j] = NULL;
175
176         *mapped_attrs = na;
177         return LDAP_SUCCESS;
178 }
179
180 int
181 map_attr_value(
182                 dncookie                *dc,
183                 AttributeDescription    *ad,
184                 struct berval           *mapped_attr,
185                 struct berval           *value,
186                 struct berval           *mapped_value,
187                 int                     remap )
188 {
189         struct berval           vtmp;
190         int                     freeval = 0;
191
192         ldap_back_map( &dc->rwmap->rwm_at, &ad->ad_cname, mapped_attr, remap );
193         if ( mapped_attr->bv_val == NULL || mapped_attr->bv_val[0] == '\0') {
194                 /*
195                  * FIXME: are we sure we need to search oc_map if at_map fails?
196                  */
197                 ldap_back_map( &dc->rwmap->rwm_oc, &ad->ad_cname, mapped_attr, remap );
198                 if ( mapped_attr->bv_val == NULL || mapped_attr->bv_val[0] == '\0' ) {
199                         *mapped_attr = ad->ad_cname;
200                 }
201         }
202
203         if ( value == NULL ) {
204                 return 0;
205         }
206
207         if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
208         {
209                 dncookie fdc = *dc;
210
211 #ifdef ENABLE_REWRITE
212                 fdc.ctx = "searchFilterAttrDN";
213 #endif
214
215                 switch ( ldap_back_dn_massage( &fdc, value, &vtmp ) ) {
216                 case LDAP_SUCCESS:
217                         if ( vtmp.bv_val != value->bv_val ) {
218                                 freeval = 1;
219                         }
220                         break;
221                 
222                 case LDAP_UNWILLING_TO_PERFORM:
223                         return -1;
224
225                 case LDAP_OTHER:
226                         return -1;
227                 }
228
229         } else if ( ad == slap_schema.si_ad_objectClass || ad == slap_schema.si_ad_structuralObjectClass ) {
230                 ldap_back_map( &dc->rwmap->rwm_oc, value, &vtmp, remap );
231                 if ( vtmp.bv_val == NULL || vtmp.bv_val[0] == '\0' ) {
232                         vtmp = *value;
233                 }
234                 
235         } else {
236                 vtmp = *value;
237         }
238
239         filter_escape_value( &vtmp, mapped_value );
240
241         if ( freeval ) {
242                 ber_memfree( vtmp.bv_val );
243         }
244         
245         return 0;
246 }
247
248 static int
249 ldap_back_int_filter_map_rewrite(
250                 dncookie                *dc,
251                 Filter                  *f,
252                 struct berval           *fstr,
253                 int                     remap )
254 {
255         int             i;
256         Filter          *p;
257         struct berval   atmp;
258         struct berval   vtmp;
259         ber_len_t       len;
260
261         if ( f == NULL ) {
262                 ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr );
263                 return -1;
264         }
265
266         switch ( f->f_choice ) {
267         case LDAP_FILTER_EQUALITY:
268                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
269                                         &f->f_av_value, &vtmp, remap ) )
270                 {
271                         return -1;
272                 }
273
274                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
275                         + ( sizeof("(=)") - 1 );
276                 fstr->bv_val = malloc( fstr->bv_len + 1 );
277
278                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
279                         atmp.bv_val, vtmp.bv_val );
280
281                 ber_memfree( vtmp.bv_val );
282                 break;
283
284         case LDAP_FILTER_GE:
285                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
286                                         &f->f_av_value, &vtmp, remap ) )
287                 {
288                         return -1;
289                 }
290
291                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
292                         + ( sizeof("(>=)") - 1 );
293                 fstr->bv_val = malloc( fstr->bv_len + 1 );
294
295                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
296                         atmp.bv_val, vtmp.bv_val );
297
298                 ber_memfree( vtmp.bv_val );
299                 break;
300
301         case LDAP_FILTER_LE:
302                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
303                                         &f->f_av_value, &vtmp, remap ) )
304                 {
305                         return -1;
306                 }
307
308                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
309                         + ( sizeof("(<=)") - 1 );
310                 fstr->bv_val = malloc( fstr->bv_len + 1 );
311
312                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
313                         atmp.bv_val, vtmp.bv_val );
314
315                 ber_memfree( vtmp.bv_val );
316                 break;
317
318         case LDAP_FILTER_APPROX:
319                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
320                                         &f->f_av_value, &vtmp, remap ) )
321                 {
322                         return -1;
323                 }
324
325                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
326                         + ( sizeof("(~=)") - 1 );
327                 fstr->bv_val = malloc( fstr->bv_len + 1 );
328
329                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
330                         atmp.bv_val, vtmp.bv_val );
331
332                 ber_memfree( vtmp.bv_val );
333                 break;
334
335         case LDAP_FILTER_SUBSTRINGS:
336                 if ( map_attr_value( dc, f->f_sub_desc, &atmp,
337                                         NULL, NULL, remap ) )
338                 {
339                         return -1;
340                 }
341
342                 /* cannot be a DN ... */
343
344                 fstr->bv_len = atmp.bv_len + ( sizeof("(=*)") - 1 );
345                 fstr->bv_val = malloc( fstr->bv_len + 128 );
346
347                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
348                         atmp.bv_val );
349
350                 if ( f->f_sub_initial.bv_val != NULL ) {
351                         len = fstr->bv_len;
352
353                         filter_escape_value( &f->f_sub_initial, &vtmp );
354
355                         fstr->bv_len += vtmp.bv_len;
356                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
357
358                         snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
359                                 /* "(attr=" */ "%s*)",
360                                 vtmp.bv_val );
361
362                         ber_memfree( vtmp.bv_val );
363                 }
364
365                 if ( f->f_sub_any != NULL ) {
366                         for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) {
367                                 len = fstr->bv_len;
368                                 filter_escape_value( &f->f_sub_any[i], &vtmp );
369
370                                 fstr->bv_len += vtmp.bv_len + 1;
371                                 fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
372
373                                 snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
374                                         /* "(attr=[init]*[any*]" */ "%s*)",
375                                         vtmp.bv_val );
376                                 ber_memfree( vtmp.bv_val );
377                         }
378                 }
379
380                 if ( f->f_sub_final.bv_val != NULL ) {
381                         len = fstr->bv_len;
382
383                         filter_escape_value( &f->f_sub_final, &vtmp );
384
385                         fstr->bv_len += vtmp.bv_len;
386                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
387
388                         snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
389                                 /* "(attr=[init*][any*]" */ "%s)",
390                                 vtmp.bv_val );
391
392                         ber_memfree( vtmp.bv_val );
393                 }
394
395                 break;
396
397         case LDAP_FILTER_PRESENT:
398                 if ( map_attr_value( dc, f->f_desc, &atmp,
399                                         NULL, NULL, remap ) )
400                 {
401                         return -1;
402                 }
403
404                 fstr->bv_len = atmp.bv_len + ( sizeof("(=*)") - 1 );
405                 fstr->bv_val = malloc( fstr->bv_len + 1 );
406
407                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
408                         atmp.bv_val );
409                 break;
410
411         case LDAP_FILTER_AND:
412         case LDAP_FILTER_OR:
413         case LDAP_FILTER_NOT:
414                 fstr->bv_len = sizeof("(%)") - 1;
415                 fstr->bv_val = malloc( fstr->bv_len + 128 );
416
417                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
418                         f->f_choice == LDAP_FILTER_AND ? '&' :
419                         f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
420
421                 for ( p = f->f_list; p != NULL; p = p->f_next ) {
422                         len = fstr->bv_len;
423
424                         if ( ldap_back_int_filter_map_rewrite( dc, p, &vtmp, remap ) )
425                         {
426                                 return -1;
427                         }
428                         
429                         fstr->bv_len += vtmp.bv_len;
430                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
431
432                         snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2, 
433                                 /*"("*/ "%s)", vtmp.bv_val );
434
435                         ch_free( vtmp.bv_val );
436                 }
437
438                 break;
439
440         case LDAP_FILTER_EXT: {
441                 if ( f->f_mr_desc ) {
442                         if ( map_attr_value( dc, f->f_mr_desc, &atmp,
443                                                 &f->f_mr_value, &vtmp, remap ) )
444                         {
445                                 return -1;
446                         }
447
448                 } else {
449                         atmp.bv_len = 0;
450                         atmp.bv_val = "";
451                         
452                         filter_escape_value( &f->f_mr_value, &vtmp );
453                 }
454                         
455
456                 fstr->bv_len = atmp.bv_len +
457                         ( f->f_mr_dnattrs ? sizeof(":dn")-1 : 0 ) +
458                         ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len+1 : 0 ) +
459                         vtmp.bv_len + ( sizeof("(:=)") - 1 );
460                 fstr->bv_val = malloc( fstr->bv_len + 1 );
461
462                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
463                         atmp.bv_val,
464                         f->f_mr_dnattrs ? ":dn" : "",
465                         f->f_mr_rule_text.bv_len ? ":" : "",
466                         f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "",
467                         vtmp.bv_val );
468                 ber_memfree( vtmp.bv_val );
469                 } break;
470
471         case SLAPD_FILTER_COMPUTED:
472                 switch ( f->f_result ) {
473                 case LDAP_COMPARE_FALSE:
474                         ber_str2bv( "(?=false)", STRLENOF( "(?=false)" ), 1, fstr );
475                         break;
476                 case LDAP_COMPARE_TRUE:
477                         ber_str2bv( "(?=true)", STRLENOF( "(?=true)" ), 1, fstr );
478                         break;
479                 case SLAPD_COMPARE_UNDEFINED:
480                         ber_str2bv( "(?=undefined)", STRLENOF( "(?=undefined)" ), 1, fstr );
481                         break;
482                 default:
483                         ber_str2bv( "(?=error)", STRLENOF( "(?=error)" ), 1, fstr );
484                         break;
485                 }
486                 break;
487
488         default:
489                 ber_str2bv( "(?=unknown)", STRLENOF( "(?=unknown)" ), 1, fstr );
490                 break;
491         }
492
493         return 0;
494 }
495
496 int
497 ldap_back_filter_map_rewrite(
498                 dncookie                *dc,
499                 Filter                  *f,
500                 struct berval           *fstr,
501                 int                     remap )
502 {
503         int             rc;
504         dncookie        fdc;
505         struct berval   ftmp;
506
507         rc = ldap_back_int_filter_map_rewrite( dc, f, fstr, remap );
508
509 #ifdef ENABLE_REWRITE
510         if ( rc != LDAP_SUCCESS ) {
511                 return rc;
512         }
513
514         fdc = *dc;
515         ftmp = *fstr;
516
517         fdc.ctx = "searchFilter";
518         
519         switch ( rewrite_session( fdc.rwmap->rwm_rw, fdc.ctx,
520                                 ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : "" ),
521                                 fdc.conn, &fstr->bv_val ) )
522         {
523         case REWRITE_REGEXEC_OK:
524                 if ( !BER_BVISNULL( fstr ) ) {
525                         fstr->bv_len = strlen( fstr->bv_val );
526                 } else {
527                         *fstr = ftmp;
528                 }
529                 Debug( LDAP_DEBUG_ARGS,
530                         "[rw] %s: \"%s\" -> \"%s\"\n",
531                         fdc.ctx, ftmp.bv_val, fstr->bv_val );           
532                 rc = LDAP_SUCCESS;
533                 break;
534                 
535         case REWRITE_REGEXEC_UNWILLING:
536                 if ( fdc.rs ) {
537                         fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
538                         fdc.rs->sr_text = "Operation not allowed";
539                 }
540                 rc = LDAP_UNWILLING_TO_PERFORM;
541                 break;
542                 
543         case REWRITE_REGEXEC_ERR:
544                 if ( fdc.rs ) {
545                         fdc.rs->sr_err = LDAP_OTHER;
546                         fdc.rs->sr_text = "Rewrite error";
547                 }
548                 rc = LDAP_OTHER;
549                 break;
550         }
551 #endif /* ENABLE_REWRITE */
552
553         return rc;
554 }
555
556 int
557 ldap_back_referral_result_rewrite(
558         dncookie                *dc,
559         BerVarray               a_vals
560 )
561 {
562         int             i, last;
563
564         assert( dc );
565         assert( a_vals );
566
567         for ( last = 0; !BER_BVISNULL( &a_vals[ last ] ); last++ )
568                 ;
569         last--;
570
571         for ( i = 0; !BER_BVISNULL( &a_vals[ i ] ); i++ ) {
572                 struct berval   dn, olddn;
573                 int             rc;
574                 LDAPURLDesc     *ludp;
575
576                 rc = ldap_url_parse( a_vals[ i ].bv_val, &ludp );
577                 if ( rc != LDAP_URL_SUCCESS ) {
578                         /* leave attr untouched if massage failed */
579                         continue;
580                 }
581
582                 ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
583                 
584                 rc = ldap_back_dn_massage( dc, &olddn, &dn );
585                 switch ( rc ) {
586                 case LDAP_UNWILLING_TO_PERFORM:
587                         /*
588                          * FIXME: need to check if it may be considered 
589                          * legal to trim values when adding/modifying;
590                          * it should be when searching (e.g. ACLs).
591                          */
592                         LBER_FREE( a_vals[ i ].bv_val );
593                         if ( last > i ) {
594                                 a_vals[ i ] = a_vals[ last ];
595                         }
596                         BER_BVZERO( &a_vals[ last ] );
597                         last--;
598                         i--;
599                         break;
600
601                 default:
602                         /* leave attr untouched if massage failed */
603                         if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val )
604                         {
605                                 char    *newurl;
606
607                                 ludp->lud_dn = dn.bv_val;
608                                 newurl = ldap_url_desc2str( ludp );
609                                 if ( newurl == NULL ) {
610                                         /* FIXME: leave attr untouched
611                                          * even if ldap_url_desc2str failed... */
612                                         break;
613                                 }
614
615                                 LBER_FREE( a_vals[ i ].bv_val );
616                                 ber_str2bv( newurl, 0, 1, &a_vals[ i ] );
617                                 LDAP_FREE( newurl );
618                                 ludp->lud_dn = olddn.bv_val;
619                         }
620                         break;
621                 }
622
623                 ldap_free_urldesc( ludp );
624         }
625
626         return 0;
627 }
628
629 /*
630  * I don't like this much, but we need two different
631  * functions because different heap managers may be
632  * in use in back-ldap/meta to reduce the amount of
633  * calls to malloc routines, and some of the free()
634  * routines may be macros with args
635  */
636 int
637 ldap_dnattr_rewrite(
638         dncookie                *dc,
639         BerVarray               a_vals
640 )
641 {
642         struct berval   bv;
643         int             i, last;
644
645         for ( last = 0; a_vals[last].bv_val != NULL; last++ );
646         last--;
647
648         for ( i = 0; a_vals[i].bv_val != NULL; i++ ) {
649                 switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
650                 case LDAP_UNWILLING_TO_PERFORM:
651                         /*
652                          * FIXME: need to check if it may be considered 
653                          * legal to trim values when adding/modifying;
654                          * it should be when searching (e.g. ACLs).
655                          */
656                         ch_free( a_vals[i].bv_val );
657                         if (last > i ) {
658                                 a_vals[i] = a_vals[last];
659                         }
660                         a_vals[last].bv_len = 0;
661                         a_vals[last].bv_val = NULL;
662                         last--;
663                         break;
664
665                 default:
666                         /* leave attr untouched if massage failed */
667                         if ( bv.bv_val && bv.bv_val != a_vals[i].bv_val ) {
668                                 ch_free( a_vals[i].bv_val );
669                                 a_vals[i] = bv;
670                         }
671                         break;
672                 }
673         }
674         
675         return 0;
676 }
677
678 int
679 ldap_dnattr_result_rewrite(
680         dncookie                *dc,
681         BerVarray               a_vals
682 )
683 {
684         struct berval   bv;
685         int             i, last;
686
687         for ( last = 0; a_vals[last].bv_val; last++ );
688         last--;
689
690         for ( i = 0; a_vals[i].bv_val; i++ ) {
691                 switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
692                 case LDAP_UNWILLING_TO_PERFORM:
693                         /*
694                          * FIXME: need to check if it may be considered 
695                          * legal to trim values when adding/modifying;
696                          * it should be when searching (e.g. ACLs).
697                          */
698                         LBER_FREE( a_vals[i].bv_val );
699                         if ( last > i ) {
700                                 a_vals[i] = a_vals[last];
701                         }
702                         BER_BVZERO( &a_vals[last] );
703                         last--;
704                         break;
705
706                 default:
707                         /* leave attr untouched if massage failed */
708                         if ( bv.bv_val && a_vals[i].bv_val != bv.bv_val ) {
709                                 LBER_FREE( a_vals[i].bv_val );
710                                 a_vals[i] = bv;
711                         }
712                         break;
713                 }
714         }
715
716         return 0;
717 }
718