]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/map.c
a607369b7edbd2c5db1ad829f2dcb95fb3285e91
[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-2005 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
125         BER_BVZERO( bv );
126         fmapping.src = *s;
127         mapping = (struct ldapmapping *)avl_find( tree, (caddr_t)&fmapping, mapping_cmp );
128         if ( mapping != NULL ) {
129                 if ( !BER_BVISNULL( &mapping->dst ) ) {
130                         *bv = mapping->dst;
131                 }
132                 return;
133         }
134
135         if ( !map->drop_missing ) {
136                 *bv = *s;
137         }
138
139         return;
140 }
141
142 int
143 ldap_back_map_attrs(
144                 struct ldapmap *at_map,
145                 AttributeName *an,
146                 int remap,
147                 char ***mapped_attrs
148 )
149 {
150         int i, j;
151         char **na;
152         struct berval mapped;
153
154         if ( an == NULL ) {
155                 *mapped_attrs = NULL;
156                 return LDAP_SUCCESS;
157         }
158
159         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
160                 /*  */ ;
161
162         na = (char **)ch_calloc( i + 1, sizeof(char *) );
163         if ( na == NULL ) {
164                 *mapped_attrs = NULL;
165                 return LDAP_NO_MEMORY;
166         }
167
168         for ( i = j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
169                 ldap_back_map( at_map, &an[i].an_name, &mapped, remap );
170                 if ( !BER_BVISNULL( &mapped ) && !BER_BVISEMPTY( &mapped ) ) {
171                         na[j++] = mapped.bv_val;
172                 }
173         }
174         if ( j == 0 && i != 0 ) {
175                 na[j++] = LDAP_NO_ATTRS;
176         }
177         na[j] = NULL;
178
179         *mapped_attrs = na;
180         return LDAP_SUCCESS;
181 }
182
183 int
184 map_attr_value(
185                 dncookie                *dc,
186                 AttributeDescription    *ad,
187                 struct berval           *mapped_attr,
188                 struct berval           *value,
189                 struct berval           *mapped_value,
190                 int                     remap )
191 {
192         struct berval           vtmp;
193         int                     freeval = 0;
194
195         ldap_back_map( &dc->rwmap->rwm_at, &ad->ad_cname, mapped_attr, remap );
196         if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
197                 /*
198                  * FIXME: are we sure we need to search oc_map if at_map fails?
199                  */
200                 ldap_back_map( &dc->rwmap->rwm_oc, &ad->ad_cname, mapped_attr, remap );
201                 if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
202                         *mapped_attr = ad->ad_cname;
203                 }
204         }
205
206         if ( value == NULL ) {
207                 return 0;
208         }
209
210         if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
211         {
212                 dncookie fdc = *dc;
213
214 #ifdef ENABLE_REWRITE
215                 fdc.ctx = "searchFilterAttrDN";
216 #endif
217
218                 switch ( ldap_back_dn_massage( &fdc, value, &vtmp ) ) {
219                 case LDAP_SUCCESS:
220                         if ( vtmp.bv_val != value->bv_val ) {
221                                 freeval = 1;
222                         }
223                         break;
224                 
225                 case LDAP_UNWILLING_TO_PERFORM:
226                         return -1;
227
228                 case LDAP_OTHER:
229                         return -1;
230                 }
231
232         } else if ( ad == slap_schema.si_ad_objectClass || ad == slap_schema.si_ad_structuralObjectClass ) {
233                 ldap_back_map( &dc->rwmap->rwm_oc, value, &vtmp, remap );
234                 if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
235                         vtmp = *value;
236                 }
237                 
238         } else {
239                 vtmp = *value;
240         }
241
242         filter_escape_value( &vtmp, mapped_value );
243
244         if ( freeval ) {
245                 ber_memfree( vtmp.bv_val );
246         }
247         
248         return 0;
249 }
250
251 static int
252 ldap_back_int_filter_map_rewrite(
253                 dncookie                *dc,
254                 Filter                  *f,
255                 struct berval           *fstr,
256                 int                     remap )
257 {
258         int             i;
259         Filter          *p;
260         struct berval   atmp;
261         struct berval   vtmp;
262         ber_len_t       len;
263
264         if ( f == NULL ) {
265                 ber_str2bv( "No filter!", sizeof("No filter!")-1, 1, fstr );
266                 return -1;
267         }
268
269         switch ( f->f_choice ) {
270         case LDAP_FILTER_EQUALITY:
271                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
272                                         &f->f_av_value, &vtmp, remap ) )
273                 {
274                         return -1;
275                 }
276
277                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
278                         + ( sizeof("(=)") - 1 );
279                 fstr->bv_val = malloc( fstr->bv_len + 1 );
280
281                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
282                         atmp.bv_val, vtmp.bv_val );
283
284                 ber_memfree( vtmp.bv_val );
285                 break;
286
287         case LDAP_FILTER_GE:
288                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
289                                         &f->f_av_value, &vtmp, remap ) )
290                 {
291                         return -1;
292                 }
293
294                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
295                         + ( sizeof("(>=)") - 1 );
296                 fstr->bv_val = malloc( fstr->bv_len + 1 );
297
298                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
299                         atmp.bv_val, vtmp.bv_val );
300
301                 ber_memfree( vtmp.bv_val );
302                 break;
303
304         case LDAP_FILTER_LE:
305                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
306                                         &f->f_av_value, &vtmp, remap ) )
307                 {
308                         return -1;
309                 }
310
311                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
312                         + ( sizeof("(<=)") - 1 );
313                 fstr->bv_val = malloc( fstr->bv_len + 1 );
314
315                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
316                         atmp.bv_val, vtmp.bv_val );
317
318                 ber_memfree( vtmp.bv_val );
319                 break;
320
321         case LDAP_FILTER_APPROX:
322                 if ( map_attr_value( dc, f->f_av_desc, &atmp,
323                                         &f->f_av_value, &vtmp, remap ) )
324                 {
325                         return -1;
326                 }
327
328                 fstr->bv_len = atmp.bv_len + vtmp.bv_len
329                         + ( sizeof("(~=)") - 1 );
330                 fstr->bv_val = malloc( fstr->bv_len + 1 );
331
332                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
333                         atmp.bv_val, vtmp.bv_val );
334
335                 ber_memfree( vtmp.bv_val );
336                 break;
337
338         case LDAP_FILTER_SUBSTRINGS:
339                 if ( map_attr_value( dc, f->f_sub_desc, &atmp,
340                                         NULL, NULL, remap ) )
341                 {
342                         return -1;
343                 }
344
345                 /* cannot be a DN ... */
346
347                 fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
348                 fstr->bv_val = malloc( fstr->bv_len + 128 ); /* FIXME: why 128 ? */
349
350                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
351                         atmp.bv_val );
352
353                 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
354                         len = fstr->bv_len;
355
356                         filter_escape_value( &f->f_sub_initial, &vtmp );
357
358                         fstr->bv_len += vtmp.bv_len;
359                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
360
361                         snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
362                                 /* "(attr=" */ "%s*)",
363                                 vtmp.bv_val );
364
365                         ber_memfree( vtmp.bv_val );
366                 }
367
368                 if ( f->f_sub_any != NULL ) {
369                         for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
370                                 len = fstr->bv_len;
371                                 filter_escape_value( &f->f_sub_any[i], &vtmp );
372
373                                 fstr->bv_len += vtmp.bv_len + 1;
374                                 fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
375
376                                 snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
377                                         /* "(attr=[init]*[any*]" */ "%s*)",
378                                         vtmp.bv_val );
379                                 ber_memfree( vtmp.bv_val );
380                         }
381                 }
382
383                 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
384                         len = fstr->bv_len;
385
386                         filter_escape_value( &f->f_sub_final, &vtmp );
387
388                         fstr->bv_len += vtmp.bv_len;
389                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
390
391                         snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
392                                 /* "(attr=[init*][any*]" */ "%s)",
393                                 vtmp.bv_val );
394
395                         ber_memfree( vtmp.bv_val );
396                 }
397
398                 break;
399
400         case LDAP_FILTER_PRESENT:
401                 if ( map_attr_value( dc, f->f_desc, &atmp,
402                                         NULL, NULL, remap ) )
403                 {
404                         return -1;
405                 }
406
407                 fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
408                 fstr->bv_val = malloc( fstr->bv_len + 1 );
409
410                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
411                         atmp.bv_val );
412                 break;
413
414         case LDAP_FILTER_AND:
415         case LDAP_FILTER_OR:
416         case LDAP_FILTER_NOT:
417                 fstr->bv_len = STRLENOF( "(%)" );
418                 fstr->bv_val = malloc( fstr->bv_len + 128 );    /* FIXME: why 128? */
419
420                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
421                         f->f_choice == LDAP_FILTER_AND ? '&' :
422                         f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
423
424                 for ( p = f->f_list; p != NULL; p = p->f_next ) {
425                         len = fstr->bv_len;
426
427                         if ( ldap_back_int_filter_map_rewrite( dc, p, &vtmp, remap ) )
428                         {
429                                 return -1;
430                         }
431                         
432                         fstr->bv_len += vtmp.bv_len;
433                         fstr->bv_val = ch_realloc( fstr->bv_val, fstr->bv_len + 1 );
434
435                         snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2, 
436                                 /*"("*/ "%s)", vtmp.bv_val );
437
438                         ch_free( vtmp.bv_val );
439                 }
440
441                 break;
442
443         case LDAP_FILTER_EXT: {
444                 if ( f->f_mr_desc ) {
445                         if ( map_attr_value( dc, f->f_mr_desc, &atmp,
446                                                 &f->f_mr_value, &vtmp, remap ) )
447                         {
448                                 return -1;
449                         }
450
451                 } else {
452                         BER_BVSTR( &atmp, "" );
453                         filter_escape_value( &f->f_mr_value, &vtmp );
454                 }
455
456                 /* FIXME: cleanup (less ?: operators...) */
457                 fstr->bv_len = atmp.bv_len +
458                         ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
459                         ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
460                         vtmp.bv_len + ( STRLENOF( "(:=)" ) );
461                 fstr->bv_val = malloc( fstr->bv_len + 1 );
462
463                 snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
464                         atmp.bv_val,
465                         f->f_mr_dnattrs ? ":dn" : "",
466                         !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
467                         !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
468                         vtmp.bv_val );
469                 ber_memfree( vtmp.bv_val );
470                 } break;
471
472         case SLAPD_FILTER_COMPUTED:
473                 switch ( f->f_result ) {
474                 case LDAP_COMPARE_FALSE:
475                         ber_str2bv( "(?=false)", STRLENOF( "(?=false)" ), 1, fstr );
476                         break;
477                 case LDAP_COMPARE_TRUE:
478                         ber_str2bv( "(?=true)", STRLENOF( "(?=true)" ), 1, fstr );
479                         break;
480                 case SLAPD_COMPARE_UNDEFINED:
481                         ber_str2bv( "(?=undefined)", STRLENOF( "(?=undefined)" ), 1, fstr );
482                         break;
483                 default:
484                         ber_str2bv( "(?=error)", STRLENOF( "(?=error)" ), 1, fstr );
485                         break;
486                 }
487                 break;
488
489         default:
490                 ber_str2bv( "(?=unknown)", STRLENOF( "(?=unknown)" ), 1, fstr );
491                 break;
492         }
493
494         return 0;
495 }
496
497 int
498 ldap_back_filter_map_rewrite(
499                 dncookie                *dc,
500                 Filter                  *f,
501                 struct berval           *fstr,
502                 int                     remap )
503 {
504         int             rc;
505         dncookie        fdc;
506         struct berval   ftmp;
507
508         rc = ldap_back_int_filter_map_rewrite( dc, f, fstr, remap );
509
510 #ifdef ENABLE_REWRITE
511         if ( rc != LDAP_SUCCESS ) {
512                 return rc;
513         }
514
515         fdc = *dc;
516         ftmp = *fstr;
517
518         fdc.ctx = "searchFilter";
519         
520         switch ( rewrite_session( fdc.rwmap->rwm_rw, fdc.ctx,
521                                 ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : "" ),
522                                 fdc.conn, &fstr->bv_val ) )
523         {
524         case REWRITE_REGEXEC_OK:
525                 if ( !BER_BVISNULL( fstr ) ) {
526                         fstr->bv_len = strlen( fstr->bv_val );
527
528                 } else {
529                         *fstr = ftmp;
530                 }
531                 Debug( LDAP_DEBUG_ARGS,
532                         "[rw] %s: \"%s\" -> \"%s\"\n",
533                         fdc.ctx, ftmp.bv_val, fstr->bv_val );           
534                 rc = LDAP_SUCCESS;
535                 break;
536                 
537         case REWRITE_REGEXEC_UNWILLING:
538                 if ( fdc.rs ) {
539                         fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
540                         fdc.rs->sr_text = "Operation not allowed";
541                 }
542                 rc = LDAP_UNWILLING_TO_PERFORM;
543                 break;
544                 
545         case REWRITE_REGEXEC_ERR:
546                 if ( fdc.rs ) {
547                         fdc.rs->sr_err = LDAP_OTHER;
548                         fdc.rs->sr_text = "Rewrite error";
549                 }
550                 rc = LDAP_OTHER;
551                 break;
552         }
553 #endif /* ENABLE_REWRITE */
554
555         return rc;
556 }
557
558 int
559 ldap_back_referral_result_rewrite(
560         dncookie                *dc,
561         BerVarray               a_vals
562 )
563 {
564         int             i, last;
565
566         assert( dc );
567         assert( a_vals );
568
569         for ( last = 0; !BER_BVISNULL( &a_vals[ last ] ); last++ )
570                 ;
571         last--;
572
573         for ( i = 0; !BER_BVISNULL( &a_vals[ i ] ); i++ ) {
574                 struct berval   dn, olddn;
575                 int             rc;
576                 LDAPURLDesc     *ludp;
577
578                 rc = ldap_url_parse( a_vals[ i ].bv_val, &ludp );
579                 if ( rc != LDAP_URL_SUCCESS ) {
580                         /* leave attr untouched if massage failed */
581                         continue;
582                 }
583
584                 /* FIXME: URLs like "ldap:///dc=suffix" if passed
585                  * thru ldap_url_parse() and ldap_url_desc2str()
586                  * get rewritten as "ldap:///dc=suffix??base";
587                  * we don't want this to occur... */
588                 if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
589                         ludp->lud_scope = LDAP_SCOPE_DEFAULT;
590                 }
591
592                 ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
593                 
594                 rc = ldap_back_dn_massage( dc, &olddn, &dn );
595                 switch ( rc ) {
596                 case LDAP_UNWILLING_TO_PERFORM:
597                         /*
598                          * FIXME: need to check if it may be considered 
599                          * legal to trim values when adding/modifying;
600                          * it should be when searching (e.g. ACLs).
601                          */
602                         LBER_FREE( a_vals[ i ].bv_val );
603                         if ( last > i ) {
604                                 a_vals[ i ] = a_vals[ last ];
605                         }
606                         BER_BVZERO( &a_vals[ last ] );
607                         last--;
608                         i--;
609                         break;
610
611                 default:
612                         /* leave attr untouched if massage failed */
613                         if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val )
614                         {
615                                 char    *newurl;
616
617                                 ludp->lud_dn = dn.bv_val;
618                                 newurl = ldap_url_desc2str( ludp );
619                                 if ( newurl == NULL ) {
620                                         /* FIXME: leave attr untouched
621                                          * even if ldap_url_desc2str failed...
622                                          */
623                                         break;
624                                 }
625
626                                 LBER_FREE( a_vals[ i ].bv_val );
627                                 ber_str2bv( newurl, 0, 1, &a_vals[ i ] );
628                                 LDAP_FREE( newurl );
629                                 ludp->lud_dn = olddn.bv_val;
630                         }
631                         break;
632                 }
633
634                 ldap_free_urldesc( ludp );
635         }
636
637         return 0;
638 }
639
640 /*
641  * I don't like this much, but we need two different
642  * functions because different heap managers may be
643  * in use in back-ldap/meta to reduce the amount of
644  * calls to malloc routines, and some of the free()
645  * routines may be macros with args
646  */
647 int
648 ldap_dnattr_rewrite(
649         dncookie                *dc,
650         BerVarray               a_vals
651 )
652 {
653         struct berval   bv;
654         int             i, last;
655
656         assert( a_vals != NULL );
657
658         for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
659                 ;
660         last--;
661
662         for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
663                 switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
664                 case LDAP_UNWILLING_TO_PERFORM:
665                         /*
666                          * FIXME: need to check if it may be considered 
667                          * legal to trim values when adding/modifying;
668                          * it should be when searching (e.g. ACLs).
669                          */
670                         ch_free( a_vals[i].bv_val );
671                         if ( last > i ) {
672                                 a_vals[i] = a_vals[last];
673                         }
674                         BER_BVZERO( &a_vals[last] );
675                         last--;
676                         break;
677
678                 default:
679                         /* leave attr untouched if massage failed */
680                         if ( !BER_BVISNULL( &bv ) && bv.bv_val != a_vals[i].bv_val ) {
681                                 ch_free( a_vals[i].bv_val );
682                                 a_vals[i] = bv;
683                         }
684                         break;
685                 }
686         }
687         
688         return 0;
689 }
690
691 int
692 ldap_dnattr_result_rewrite(
693         dncookie                *dc,
694         BerVarray               a_vals
695 )
696 {
697         struct berval   bv;
698         int             i, last;
699
700         assert( a_vals != NULL );
701
702         for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
703                 ;
704         last--;
705
706         for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
707                 switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
708                 case LDAP_UNWILLING_TO_PERFORM:
709                         /*
710                          * FIXME: need to check if it may be considered 
711                          * legal to trim values when adding/modifying;
712                          * it should be when searching (e.g. ACLs).
713                          */
714                         LBER_FREE( a_vals[i].bv_val );
715                         if ( last > i ) {
716                                 a_vals[i] = a_vals[last];
717                         }
718                         BER_BVZERO( &a_vals[last] );
719                         last--;
720                         break;
721
722                 default:
723                         /* leave attr untouched if massage failed */
724                         if ( !BER_BVISNULL( &bv ) && a_vals[i].bv_val != bv.bv_val ) {
725                                 LBER_FREE( a_vals[i].bv_val );
726                                 a_vals[i] = bv;
727                         }
728                         break;
729                 }
730         }
731
732         return 0;
733 }
734