]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/rwm.c
1c37890585d5113de5c16c622eb726323006d87c
[openldap] / servers / slapd / overlays / rwm.c
1 /* rwm.c - rewrite/remap operations */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2007 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Pierangelo Masarati.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17
18 #include "portable.h"
19
20 #ifdef SLAPD_OVER_RWM
21
22 #include <stdio.h>
23
24 #include <ac/string.h>
25
26 #include "slap.h"
27 #include "rwm.h"
28
29 typedef struct rwm_op_state {
30         ber_tag_t r_tag;
31         struct berval ro_dn;
32         struct berval ro_ndn;
33         struct berval r_dn;
34         struct berval r_ndn;
35         AttributeName *mapped_attrs;
36         OpRequest o_request;
37 } rwm_op_state;
38
39 static int
40 rwm_db_destroy( BackendDB *be );
41
42 typedef struct rwm_op_cb {
43         slap_callback cb;
44         rwm_op_state ros;
45 } rwm_op_cb;
46
47 static int
48 rwm_op_cleanup( Operation *op, SlapReply *rs )
49 {
50         slap_callback   *cb = op->o_callback;
51         rwm_op_state *ros = cb->sc_private;
52
53         if ( rs->sr_type == REP_RESULT || rs->sr_type == REP_EXTENDED ||
54                 op->o_abandon || rs->sr_err == SLAPD_ABANDON ) {
55
56                 op->o_req_dn = ros->ro_dn;
57                 op->o_req_ndn = ros->ro_ndn;
58
59                 if ( !BER_BVISNULL( &ros->r_dn )
60                         && ros->r_dn.bv_val != ros->r_ndn.bv_val )
61                 {
62                         ch_free( ros->r_dn.bv_val );
63                         BER_BVZERO( &ros->r_dn );
64                 }
65
66                 if ( !BER_BVISNULL( &ros->r_ndn ) ) {
67                         ch_free( ros->r_ndn.bv_val );
68                         BER_BVZERO( &ros->r_ndn );
69                 }
70
71                 switch( ros->r_tag ) {
72                 case LDAP_REQ_COMPARE:
73                         if ( op->orc_ava->aa_value.bv_val != ros->orc_ava->aa_value.bv_val )
74                                 op->o_tmpfree( op->orc_ava->aa_value.bv_val, op->o_tmpmemctx );
75                         op->orc_ava = ros->orc_ava;
76                         break;
77                 case LDAP_REQ_MODIFY:
78                         slap_mods_free( op->orm_modlist, 1 );
79                         op->orm_modlist = ros->orm_modlist;
80                         break;
81                 case LDAP_REQ_MODRDN:
82                         if ( op->orr_newSup != ros->orr_newSup ) {
83                                 ch_free( op->orr_newSup->bv_val );
84                                 ch_free( op->orr_nnewSup->bv_val );
85                                 op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
86                                 op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
87                                 op->orr_newSup = ros->orr_newSup;
88                                 op->orr_nnewSup = ros->orr_nnewSup;
89                         }
90                         break;
91                 case LDAP_REQ_SEARCH:
92                         ch_free( ros->mapped_attrs );
93                         filter_free_x( op, op->ors_filter );
94                         ch_free( op->ors_filterstr.bv_val );
95                         op->ors_attrs = ros->ors_attrs;
96                         op->ors_filter = ros->ors_filter;
97                         op->ors_filterstr = ros->ors_filterstr;
98                         break;
99                 case LDAP_REQ_EXTENDED:
100                         if ( op->ore_reqdata != ros->ore_reqdata ) {
101                                 ber_bvfree( op->ore_reqdata );
102                                 op->ore_reqdata = ros->ore_reqdata;
103                         }
104                         break;
105                 default:        break;
106                 }
107                 op->o_callback = op->o_callback->sc_next;
108                 op->o_tmpfree( cb, op->o_tmpmemctx );
109         }
110
111         return SLAP_CB_CONTINUE;
112 }
113
114 static rwm_op_cb *
115 rwm_callback_get( Operation *op, SlapReply *rs )
116 {
117         rwm_op_cb       *roc = NULL;
118
119         roc = op->o_tmpalloc( sizeof( struct rwm_op_cb ), op->o_tmpmemctx );
120         roc->cb.sc_cleanup = rwm_op_cleanup;
121         roc->cb.sc_response = NULL;
122         roc->cb.sc_next = op->o_callback;
123         roc->cb.sc_private = &roc->ros;
124         roc->ros.r_tag = op->o_tag;
125         roc->ros.ro_dn = op->o_req_dn;
126         roc->ros.ro_ndn = op->o_req_ndn;
127         roc->ros.o_request = op->o_request;
128         BER_BVZERO( &roc->ros.r_dn );
129         BER_BVZERO( &roc->ros.r_ndn );
130
131         return roc;
132 }
133
134
135 static int
136 rwm_op_dn_massage( Operation *op, SlapReply *rs, void *cookie,
137         rwm_op_state *ros )
138 {
139         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
140         struct ldaprwmap        *rwmap = 
141                         (struct ldaprwmap *)on->on_bi.bi_private;
142
143         struct berval           dn = BER_BVNULL,
144                                 ndn = BER_BVNULL;
145         int                     rc = 0;
146         dncookie                dc;
147
148         /*
149          * Rewrite the dn if needed
150          */
151         dc.rwmap = rwmap;
152 #ifdef ENABLE_REWRITE
153         dc.conn = op->o_conn;
154         dc.rs = rs;
155         dc.ctx = (char *)cookie;
156 #else /* ! ENABLE_REWRITE */
157         dc.tofrom = ((int *)cookie)[0];
158         dc.normalized = 0;
159 #endif /* ! ENABLE_REWRITE */
160
161         /* NOTE: in those cases where only the ndn is available,
162          * and the caller sets op->o_req_dn = op->o_req_ndn,
163          * only rewrite the op->o_req_ndn and use it as 
164          * op->o_req_dn as well */
165         ndn = op->o_req_ndn;
166         if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
167                 dn = op->o_req_dn;
168                 rc = rwm_dn_massage_pretty_normalize( &dc, &op->o_req_dn, &dn, &ndn );
169         } else {
170                 rc = rwm_dn_massage_normalize( &dc, &op->o_req_ndn, &ndn );
171         }
172
173         if ( rc != LDAP_SUCCESS ) {
174                 return rc;
175         }
176
177         if ( ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val && dn.bv_val == op->o_req_dn.bv_val )
178                         || ndn.bv_val == op->o_req_ndn.bv_val )
179         {
180                 return LDAP_SUCCESS;
181         }
182
183         if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
184                 op->o_req_dn = dn;
185                 ros->r_dn = dn;
186         } else {
187                 op->o_req_dn = ndn;
188         }
189         op->o_req_ndn = ndn;
190         ros->r_ndn = ndn;
191
192         return LDAP_SUCCESS;
193 }
194
195 static int
196 rwm_op_add( Operation *op, SlapReply *rs )
197 {
198         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
199         struct ldaprwmap        *rwmap = 
200                         (struct ldaprwmap *)on->on_bi.bi_private;
201
202         int                     rc,
203                                 i;
204         Attribute               **ap = NULL;
205         char                    *olddn = op->o_req_dn.bv_val;
206         int                     isupdate;
207
208         rwm_op_cb               *roc = rwm_callback_get( op, rs );
209
210 #ifdef ENABLE_REWRITE
211         rc = rwm_op_dn_massage( op, rs, "addDN", &roc->ros );
212 #else /* ! ENABLE_REWRITE */
213         rc = 1;
214         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
215 #endif /* ! ENABLE_REWRITE */
216         if ( rc != LDAP_SUCCESS ) {
217                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
218                 send_ldap_error( op, rs, rc, "addDN massage error" );
219                 return -1;
220         }
221
222         if ( olddn != op->o_req_dn.bv_val ) {
223                 ber_bvreplace( &op->ora_e->e_name, &op->o_req_dn );
224                 ber_bvreplace( &op->ora_e->e_nname, &op->o_req_ndn );
225         }
226
227         /* Count number of attributes in entry */ 
228         isupdate = be_shadow_update( op );
229         for ( i = 0, ap = &op->oq_add.rs_e->e_attrs; *ap; ) {
230                 Attribute       *a;
231
232                 if ( (*ap)->a_desc == slap_schema.si_ad_objectClass ||
233                                 (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
234                 {
235                         int             j, last;
236
237                         for ( last = 0; !BER_BVISNULL( &(*ap)->a_vals[ last ] ); last++ )
238                                         /* count values */ ;
239                         last--;
240                         for ( j = 0; !BER_BVISNULL( &(*ap)->a_vals[ j ] ); j++ ) {
241                                 struct ldapmapping      *mapping = NULL;
242
243                                 ( void )rwm_mapping( &rwmap->rwm_oc, &(*ap)->a_vals[ j ],
244                                                 &mapping, RWM_MAP );
245                                 if ( mapping == NULL ) {
246                                         if ( rwmap->rwm_at.drop_missing ) {
247                                                 /* FIXME: we allow to remove objectClasses as well;
248                                                  * if the resulting entry is inconsistent, that's
249                                                  * the relayed database's business...
250                                                  */
251                                                 ch_free( (*ap)->a_vals[ j ].bv_val );
252                                                 if ( last > j ) {
253                                                         (*ap)->a_vals[ j ] = (*ap)->a_vals[ last ];
254                                                 }
255                                                 BER_BVZERO( &(*ap)->a_vals[ last ] );
256                                                 last--;
257                                                 j--;
258                                         }
259
260                                 } else {
261                                         ch_free( (*ap)->a_vals[ j ].bv_val );
262                                         ber_dupbv( &(*ap)->a_vals[ j ], &mapping->m_dst );
263                                 }
264                         }
265
266                 } else if ( !isupdate && !get_relax( op ) && (*ap)->a_desc->ad_type->sat_no_user_mod )
267                 {
268                         goto next_attr;
269
270                 } else {
271                         struct ldapmapping      *mapping = NULL;
272
273                         ( void )rwm_mapping( &rwmap->rwm_at, &(*ap)->a_desc->ad_cname,
274                                         &mapping, RWM_MAP );
275                         if ( mapping == NULL ) {
276                                 if ( rwmap->rwm_at.drop_missing ) {
277                                         goto cleanup_attr;
278                                 }
279                         }
280
281                         if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
282                                         || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
283                         {
284                                 /*
285                                  * FIXME: rewrite could fail; in this case
286                                  * the operation should give up, right?
287                                  */
288 #ifdef ENABLE_REWRITE
289                                 rc = rwm_dnattr_rewrite( op, rs, "addAttrDN",
290                                                 (*ap)->a_vals,
291                                                 (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
292 #else /* ! ENABLE_REWRITE */
293                                 rc = 1;
294                                 rc = rwm_dnattr_rewrite( op, rs, &rc, (*ap)->a_vals,
295                                                 (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
296 #endif /* ! ENABLE_REWRITE */
297                                 if ( rc ) {
298                                         goto cleanup_attr;
299                                 }
300
301                         } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
302 #ifdef ENABLE_REWRITE
303                                 rc = rwm_referral_rewrite( op, rs, "referralAttrDN",
304                                                 (*ap)->a_vals,
305                                                 (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
306 #else /* ! ENABLE_REWRITE */
307                                 rc = 1;
308                                 rc = rwm_referral_rewrite( op, rs, &rc, (*ap)->a_vals,
309                                                 (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
310 #endif /* ! ENABLE_REWRITE */
311                                 if ( rc != LDAP_SUCCESS ) {
312                                         goto cleanup_attr;
313                                 }
314                         }
315                 
316                         if ( mapping != NULL ) {
317                                 assert( mapping->m_dst_ad != NULL );
318                                 (*ap)->a_desc = mapping->m_dst_ad;
319                         }
320                 }
321
322 next_attr:;
323                 ap = &(*ap)->a_next;
324                 continue;
325
326 cleanup_attr:;
327                 /* FIXME: leaking attribute/values? */
328                 a = *ap;
329
330                 *ap = (*ap)->a_next;
331                 attr_free( a );
332         }
333
334         op->o_callback = &roc->cb;
335
336         return SLAP_CB_CONTINUE;
337 }
338
339 #ifdef ENABLE_REWRITE
340 static int
341 rwm_conn_init( BackendDB *be, Connection *conn )
342 {
343         slap_overinst           *on = (slap_overinst *) be->bd_info;
344         struct ldaprwmap        *rwmap = 
345                         (struct ldaprwmap *)on->on_bi.bi_private;
346
347         ( void )rewrite_session_init( rwmap->rwm_rw, conn );
348
349         return SLAP_CB_CONTINUE;
350 }
351
352 static int
353 rwm_conn_destroy( BackendDB *be, Connection *conn )
354 {
355         slap_overinst           *on = (slap_overinst *) be->bd_info;
356         struct ldaprwmap        *rwmap = 
357                         (struct ldaprwmap *)on->on_bi.bi_private;
358
359         ( void )rewrite_session_delete( rwmap->rwm_rw, conn );
360
361         return SLAP_CB_CONTINUE;
362 }
363 #endif /* ENABLE_REWRITE */
364
365 static int
366 rwm_op_bind( Operation *op, SlapReply *rs )
367 {
368         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
369         int                     rc;
370
371         rwm_op_cb               *roc = rwm_callback_get( op, rs );
372
373 #ifdef ENABLE_REWRITE
374         rc = rwm_op_dn_massage( op, rs, "bindDN", &roc->ros );
375 #else /* ! ENABLE_REWRITE */
376         rc = 1;
377         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
378 #endif /* ! ENABLE_REWRITE */
379         if ( rc != LDAP_SUCCESS ) {
380                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
381                 send_ldap_error( op, rs, rc, "bindDN massage error" );
382                 return -1;
383         }
384
385         op->o_callback = &roc->cb;
386
387         return SLAP_CB_CONTINUE;
388 }
389
390 static int
391 rwm_op_unbind( Operation *op, SlapReply *rs )
392 {
393         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
394         struct ldaprwmap        *rwmap = 
395                         (struct ldaprwmap *)on->on_bi.bi_private;
396
397 #ifdef ENABLE_REWRITE
398         rewrite_session_delete( rwmap->rwm_rw, op->o_conn );
399 #endif /* ENABLE_REWRITE */
400
401         return SLAP_CB_CONTINUE;
402 }
403
404 static int
405 rwm_op_compare( Operation *op, SlapReply *rs )
406 {
407         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
408         struct ldaprwmap        *rwmap = 
409                         (struct ldaprwmap *)on->on_bi.bi_private;
410
411         int                     rc;
412         struct berval           mapped_vals[2] = { BER_BVNULL, BER_BVNULL };
413
414         rwm_op_cb               *roc = rwm_callback_get( op, rs );
415
416 #ifdef ENABLE_REWRITE
417         rc = rwm_op_dn_massage( op, rs, "compareDN", &roc->ros );
418 #else /* ! ENABLE_REWRITE */
419         rc = 1;
420         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
421 #endif /* ! ENABLE_REWRITE */
422         if ( rc != LDAP_SUCCESS ) {
423                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
424                 send_ldap_error( op, rs, rc, "compareDN massage error" );
425                 return -1;
426         }
427
428         /* if the attribute is an objectClass, try to remap its value */
429         if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass
430                         || op->orc_ava->aa_desc == slap_schema.si_ad_structuralObjectClass )
431         {
432                 rwm_map( &rwmap->rwm_oc, &op->orc_ava->aa_value,
433                                 &mapped_vals[0], RWM_MAP );
434                 if ( BER_BVISNULL( &mapped_vals[0] ) || BER_BVISEMPTY( &mapped_vals[0] ) )
435                 {
436                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
437                         send_ldap_error( op, rs, LDAP_OTHER, "compare objectClass map error" );
438                         return -1;
439
440                 } else if ( mapped_vals[0].bv_val != op->orc_ava->aa_value.bv_val ) {
441                         ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
442                                 op->o_tmpmemctx );
443                 }
444
445         } else {
446                 struct ldapmapping      *mapping = NULL;
447                 AttributeDescription    *ad = op->orc_ava->aa_desc;
448
449                 ( void )rwm_mapping( &rwmap->rwm_at, &op->orc_ava->aa_desc->ad_cname,
450                                 &mapping, RWM_MAP );
451                 if ( mapping == NULL ) {
452                         if ( rwmap->rwm_at.drop_missing ) {
453                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
454                                 send_ldap_error( op, rs, LDAP_OTHER, "compare attributeType map error" );
455                                 return -1;
456                         }
457
458                 } else {
459                         assert( mapping->m_dst_ad != NULL );
460                         ad = mapping->m_dst_ad;
461                 }
462
463                 if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
464                                 || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
465                 {
466                         struct berval   *mapped_valsp[2];
467                         
468                         mapped_valsp[0] = &mapped_vals[0];
469                         mapped_valsp[1] = &mapped_vals[1];
470
471                         mapped_vals[0] = op->orc_ava->aa_value;
472
473 #ifdef ENABLE_REWRITE
474                         rc = rwm_dnattr_rewrite( op, rs, "compareAttrDN", NULL, mapped_valsp );
475 #else /* ! ENABLE_REWRITE */
476                         rc = 1;
477                         rc = rwm_dnattr_rewrite( op, rs, &rc, NULL, mapped_valsp );
478 #endif /* ! ENABLE_REWRITE */
479
480                         if ( rc != LDAP_SUCCESS ) {
481                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
482                                 send_ldap_error( op, rs, rc, "compareAttrDN massage error" );
483                                 return -1;
484                         }
485
486                         if ( mapped_vals[ 0 ].bv_val != op->orc_ava->aa_value.bv_val ) {
487                                 /* NOTE: if we get here, rwm_dnattr_rewrite()
488                                  * already freed the old value, so now 
489                                  * it's invalid */
490                                 ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
491                                         op->o_tmpmemctx );
492                                 ber_memfree_x( mapped_vals[ 0 ].bv_val, NULL );
493                         }
494                 }
495                 op->orc_ava->aa_desc = ad;
496         }
497
498         op->o_callback = &roc->cb;
499
500         return SLAP_CB_CONTINUE;
501 }
502
503 static int
504 rwm_op_delete( Operation *op, SlapReply *rs )
505 {
506         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
507         int                     rc;
508
509         rwm_op_cb               *roc = rwm_callback_get( op, rs );
510
511 #ifdef ENABLE_REWRITE
512         rc = rwm_op_dn_massage( op, rs, "deleteDN", &roc->ros );
513 #else /* ! ENABLE_REWRITE */
514         rc = 1;
515         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
516 #endif /* ! ENABLE_REWRITE */
517         if ( rc != LDAP_SUCCESS ) {
518                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
519                 send_ldap_error( op, rs, rc, "deleteDN massage error" );
520                 return -1;
521         }
522
523         op->o_callback = &roc->cb;
524
525         return SLAP_CB_CONTINUE;
526 }
527
528 static int
529 rwm_op_modify( Operation *op, SlapReply *rs )
530 {
531         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
532         struct ldaprwmap        *rwmap = 
533                         (struct ldaprwmap *)on->on_bi.bi_private;
534
535         int                     isupdate;
536         Modifications           **mlp;
537         int                     rc;
538
539         rwm_op_cb               *roc = rwm_callback_get( op, rs );
540
541 #ifdef ENABLE_REWRITE
542         rc = rwm_op_dn_massage( op, rs, "modifyDN", &roc->ros );
543 #else /* ! ENABLE_REWRITE */
544         rc = 1;
545         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
546 #endif /* ! ENABLE_REWRITE */
547         if ( rc != LDAP_SUCCESS ) {
548                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
549                 send_ldap_error( op, rs, rc, "modifyDN massage error" );
550                 return -1;
551         }
552
553         isupdate = be_shadow_update( op );
554         for ( mlp = &op->oq_modify.rs_modlist; *mlp; ) {
555                 int                     is_oc = 0;
556                 Modifications           *ml;
557                 struct ldapmapping      *mapping = NULL;
558
559                 /* duplicate the modlist */
560                 ml = ch_malloc( sizeof( Modifications ));
561                 *ml = **mlp;
562                 *mlp = ml;
563
564                 if ( ml->sml_desc == slap_schema.si_ad_objectClass 
565                                 || ml->sml_desc == slap_schema.si_ad_structuralObjectClass )
566                 {
567                         is_oc = 1;
568
569                 } else if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod  )
570                 {
571                         goto next_mod;
572
573                 } else {
574                         int                     drop_missing;
575
576                         drop_missing = rwm_mapping( &rwmap->rwm_at,
577                                         &ml->sml_desc->ad_cname,
578                                         &mapping, RWM_MAP );
579                         if ( drop_missing || ( mapping != NULL && BER_BVISNULL( &mapping->m_dst ) ) )
580                         {
581                                 goto cleanup_mod;
582                         }
583                 }
584
585                 if ( ml->sml_values != NULL ) {
586                         int i, num;
587                         struct berval *bva;
588
589                         for ( num = 0; !BER_BVISNULL( &ml->sml_values[ num ] ); num++ )
590                                 /* count values */ ;
591
592                         bva = ch_malloc( (num+1) * sizeof( struct berval ));
593                         for (i=0; i<num; i++)
594                                 ber_dupbv( &bva[i], &ml->sml_values[i] );
595                         BER_BVZERO( &bva[i] );
596                         ml->sml_values = bva;
597
598                         if ( ml->sml_nvalues ) {
599                                 bva = ch_malloc( (num+1) * sizeof( struct berval ));
600                                 for (i=0; i<num; i++)
601                                         ber_dupbv( &bva[i], &ml->sml_nvalues[i] );
602                                 BER_BVZERO( &bva[i] );
603                                 ml->sml_nvalues = bva;
604                         }
605
606                         if ( is_oc ) {
607                                 int     last, j;
608
609                                 last = num-1;
610
611                                 for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
612                                         struct ldapmapping      *oc_mapping = NULL;
613                 
614                                         ( void )rwm_mapping( &rwmap->rwm_oc, &ml->sml_values[ j ],
615                                                         &oc_mapping, RWM_MAP );
616                                         if ( oc_mapping == NULL ) {
617                                                 if ( rwmap->rwm_at.drop_missing ) {
618                                                         /* FIXME: we allow to remove objectClasses as well;
619                                                          * if the resulting entry is inconsistent, that's
620                                                          * the relayed database's business...
621                                                          */
622                                                         if ( last > j ) {
623                                                                 ch_free( ml->sml_values[ j ].bv_val );
624                                                                 ml->sml_values[ j ] = ml->sml_values[ last ];
625                                                         }
626                                                         BER_BVZERO( &ml->sml_values[ last ] );
627                                                         last--;
628                                                         j--;
629                                                 }
630         
631                                         } else {
632                                                 ch_free( ml->sml_values[ j ].bv_val );
633                                                 ber_dupbv( &ml->sml_values[ j ], &oc_mapping->m_dst );
634                                         }
635                                 }
636
637                         } else {
638                                 if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
639                                                 || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
640                                 {
641 #ifdef ENABLE_REWRITE
642                                         rc = rwm_dnattr_rewrite( op, rs, "modifyAttrDN",
643                                                         ml->sml_values,
644                                                         ml->sml_nvalues ? &ml->sml_nvalues : NULL );
645 #else /* ! ENABLE_REWRITE */
646                                         rc = 1;
647                                         rc = rwm_dnattr_rewrite( op, rs, &rc, 
648                                                         ml->sml_values,
649                                                         ml->sml_nvalues ? &ml->sml_nvalues : NULL );
650 #endif /* ! ENABLE_REWRITE */
651
652                                 } else if ( ml->sml_desc == slap_schema.si_ad_ref ) {
653 #ifdef ENABLE_REWRITE
654                                         rc = rwm_referral_rewrite( op, rs,
655                                                         "referralAttrDN",
656                                                         ml->sml_values,
657                                                         ml->sml_nvalues ? &ml->sml_nvalues : NULL );
658 #else /* ! ENABLE_REWRITE */
659                                         rc = 1;
660                                         rc = rwm_referral_rewrite( op, rs, &rc,
661                                                         ml->sml_values,
662                                                         ml->sml_nvalues ? &ml->sml_nvalues : NULL );
663 #endif /* ! ENABLE_REWRITE */
664                                         if ( rc != LDAP_SUCCESS ) {
665                                                 goto cleanup_mod;
666                                         }
667                                 }
668
669                                 if ( rc != LDAP_SUCCESS ) {
670                                         goto cleanup_mod;
671                                 }
672                         }
673                 }
674
675 next_mod:;
676                 if ( mapping != NULL ) {
677                         /* use new attribute description */
678                         assert( mapping->m_dst_ad != NULL );
679                         ml->sml_desc = mapping->m_dst_ad;
680                 }
681
682                 mlp = &ml->sml_next;
683                 continue;
684
685 cleanup_mod:;
686                 ml = *mlp;
687                 *mlp = (*mlp)->sml_next;
688                 slap_mod_free( &ml->sml_mod, 0 );
689                 free( ml );
690         }
691
692         op->o_callback = &roc->cb;
693
694         return SLAP_CB_CONTINUE;
695 }
696
697 static int
698 rwm_op_modrdn( Operation *op, SlapReply *rs )
699 {
700         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
701         struct ldaprwmap        *rwmap = 
702                         (struct ldaprwmap *)on->on_bi.bi_private;
703         
704         int                     rc;
705
706         rwm_op_cb               *roc = rwm_callback_get( op, rs );
707
708         if ( op->orr_newSup ) {
709                 dncookie        dc;
710                 struct berval   nnewSup = BER_BVNULL;
711                 struct berval   newSup = BER_BVNULL;
712
713                 /*
714                  * Rewrite the new superior, if defined and required
715                  */
716                 dc.rwmap = rwmap;
717 #ifdef ENABLE_REWRITE
718                 dc.conn = op->o_conn;
719                 dc.rs = rs;
720                 dc.ctx = "newSuperiorDN";
721 #else /* ! ENABLE_REWRITE */
722                 dc.tofrom = 0;
723                 dc.normalized = 0;
724 #endif /* ! ENABLE_REWRITE */
725                 newSup = *op->orr_newSup;
726                 nnewSup = *op->orr_nnewSup;
727                 rc = rwm_dn_massage_pretty_normalize( &dc, op->orr_newSup, &newSup, &nnewSup );
728                 if ( rc != LDAP_SUCCESS ) {
729                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
730                         send_ldap_error( op, rs, rc, "newSuperiorDN massage error" );
731                         return -1;
732                 }
733
734                 if ( op->orr_newSup->bv_val != newSup.bv_val ) {
735                         op->orr_newSup = op->o_tmpalloc( sizeof( struct berval ),
736                                 op->o_tmpmemctx );
737                         op->orr_nnewSup = op->o_tmpalloc( sizeof( struct berval ),
738                                 op->o_tmpmemctx );
739                         *op->orr_newSup = newSup;
740                         *op->orr_nnewSup = nnewSup;
741                 }
742         }
743
744         /*
745          * Rewrite the dn, if needed
746          */
747 #ifdef ENABLE_REWRITE
748         rc = rwm_op_dn_massage( op, rs, "renameDN", &roc->ros );
749 #else /* ! ENABLE_REWRITE */
750         rc = 1;
751         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
752 #endif /* ! ENABLE_REWRITE */
753         if ( rc != LDAP_SUCCESS ) {
754                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
755                 send_ldap_error( op, rs, rc, "renameDN massage error" );
756                 if ( op->orr_newSup != roc->ros.orr_newSup ) {
757                         ch_free( op->orr_newSup->bv_val );
758                         ch_free( op->orr_nnewSup->bv_val );
759                         op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
760                         op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
761                         op->orr_newSup = roc->ros.orr_newSup;
762                         op->orr_nnewSup = roc->ros.orr_nnewSup;
763                 }
764                 return -1;
765         }
766
767         /* TODO: rewrite newRDN, attribute types, 
768          * values of DN-valued attributes ... */
769
770         op->o_callback = &roc->cb;
771
772         return SLAP_CB_CONTINUE;
773 }
774
775
776 static int
777 rwm_swap_attrs( Operation *op, SlapReply *rs )
778 {
779         slap_callback   *cb = op->o_callback;
780         rwm_op_state *ros = cb->sc_private;
781
782         rs->sr_attrs = ros->ors_attrs;
783
784         /* other overlays might have touched op->ors_attrs, 
785          * so we restore the original version here, otherwise
786          * attribute-mapping might fail */
787         op->ors_attrs = ros->mapped_attrs; 
788         
789         return SLAP_CB_CONTINUE;
790 }
791
792 static int
793 rwm_op_search( Operation *op, SlapReply *rs )
794 {
795         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
796         struct ldaprwmap        *rwmap = 
797                         (struct ldaprwmap *)on->on_bi.bi_private;
798
799         int                     rc;
800         dncookie                dc;
801
802         struct berval           fstr = BER_BVNULL;
803         Filter                  *f = NULL;
804
805         AttributeName           *an = NULL;
806
807         char                    *text = NULL;
808
809         rwm_op_cb               *roc = rwm_callback_get( op, rs );
810
811 #ifdef ENABLE_REWRITE
812         rc = rewrite_session_var_set( rwmap->rwm_rw, op->o_conn,
813                 "searchFilter", op->ors_filterstr.bv_val );
814         if ( rc == LDAP_SUCCESS )
815                 rc = rwm_op_dn_massage( op, rs, "searchDN", &roc->ros );
816 #else /* ! ENABLE_REWRITE */
817         rc = 1;
818         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
819 #endif /* ! ENABLE_REWRITE */
820         if ( rc != LDAP_SUCCESS ) {
821                 text = "searchDN massage error";
822                 goto error_return;
823         }
824
825         /*
826          * Rewrite the dn if needed
827          */
828         dc.rwmap = rwmap;
829 #ifdef ENABLE_REWRITE
830         dc.conn = op->o_conn;
831         dc.rs = rs;
832         dc.ctx = "searchFilterAttrDN";
833 #else /* ! ENABLE_REWRITE */
834         dc.tofrom = 0;
835         dc.normalized = 0;
836 #endif /* ! ENABLE_REWRITE */
837
838         rc = rwm_filter_map_rewrite( op, &dc, op->ors_filter, &fstr );
839         if ( rc != LDAP_SUCCESS ) {
840                 text = "searchFilter/searchFilterAttrDN massage error";
841                 goto error_return;
842         }
843
844         f = str2filter_x( op, fstr.bv_val );
845
846         if ( f == NULL ) {
847                 text = "massaged filter parse error";
848                 goto error_return;
849         }
850
851         op->ors_filter = f;
852         op->ors_filterstr = fstr;
853
854         rc = rwm_map_attrnames( &rwmap->rwm_at, &rwmap->rwm_oc,
855                         op->ors_attrs, &an, RWM_MAP );
856         if ( rc != LDAP_SUCCESS ) {
857                 text = "attribute list mapping error";
858                 goto error_return;
859         }
860
861         op->ors_attrs = an;
862         /* store the mapped Attributes for later usage, in
863          * the case that other overlays change op->ors_attrs */
864         roc->ros.mapped_attrs = an;
865         roc->cb.sc_response = rwm_swap_attrs;
866
867         op->o_callback = &roc->cb;
868
869         return SLAP_CB_CONTINUE;
870
871 error_return:;
872         if ( an != NULL ) {
873                 ch_free( an );
874         }
875
876         if ( f != NULL ) {
877                 filter_free_x( op, f );
878         }
879
880         if ( !BER_BVISNULL( &fstr ) ) {
881                 ch_free( fstr.bv_val );
882         }
883
884         op->oq_search = roc->ros.oq_search;
885
886         op->o_bd->bd_info = (BackendInfo *)on->on_info;
887         send_ldap_error( op, rs, rc, text );
888
889         return -1;
890
891 }
892
893 static int
894 rwm_exop_passwd( Operation *op, SlapReply *rs )
895 {
896         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
897         int                     rc;
898         rwm_op_cb *roc;
899
900         struct berval   id = BER_BVNULL,
901                         pwold = BER_BVNULL,
902                         pwnew = BER_BVNULL;
903         BerElement *ber = NULL;
904
905         if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
906                 return LDAP_SUCCESS;
907         }
908
909         if ( !SLAP_ISGLOBALOVERLAY( op->o_bd ) ) {
910                 rs->sr_err = LDAP_OTHER;
911                 return rs->sr_err;
912         }
913
914         rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
915                 &pwold, &pwnew, &rs->sr_text );
916         if ( rs->sr_err != LDAP_SUCCESS ) {
917                 return rs->sr_err;
918         }
919
920         if ( !BER_BVISNULL( &id ) ) {
921                 rs->sr_err = dnPrettyNormal( NULL, &id, &op->o_req_dn,
922                                 &op->o_req_ndn, op->o_tmpmemctx );
923                 if ( rs->sr_err != LDAP_SUCCESS ) {
924                         rs->sr_text = "Invalid DN";
925                         return rs->sr_err;
926                 }
927
928         } else {
929                 ber_dupbv_x( &op->o_req_dn, &op->o_dn, op->o_tmpmemctx );
930                 ber_dupbv_x( &op->o_req_ndn, &op->o_ndn, op->o_tmpmemctx );
931         }
932
933         roc = rwm_callback_get( op, rs );
934
935 #ifdef ENABLE_REWRITE
936         rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
937 #else /* ! ENABLE_REWRITE */
938         rc = 1;
939         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
940 #endif /* ! ENABLE_REWRITE */
941         if ( rc != LDAP_SUCCESS ) {
942                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
943                 send_ldap_error( op, rs, rc, "extendedDN massage error" );
944                 return -1;
945         }
946
947         ber = ber_alloc_t( LBER_USE_DER );
948         if ( !ber ) {
949                 rs->sr_err = LDAP_OTHER;
950                 rs->sr_text = "No memory";
951                 return rs->sr_err;
952         }
953         ber_printf( ber, "{" );
954         if ( !BER_BVISNULL( &id )) {
955                 ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, 
956                         &op->o_req_dn );
957         }
958         if ( !BER_BVISNULL( &pwold )) {
959                 ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &pwold );
960         }
961         if ( !BER_BVISNULL( &pwnew )) {
962                 ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &pwnew );
963         }
964         ber_printf( ber, "N}" );
965         ber_flatten( ber, &op->ore_reqdata );
966         ber_free( ber, 1 );
967
968         op->o_callback = &roc->cb;
969
970         return SLAP_CB_CONTINUE;
971 }
972
973 static struct exop {
974         struct berval   oid;
975         BI_op_extended  *extended;
976 } exop_table[] = {
977         { BER_BVC(LDAP_EXOP_MODIFY_PASSWD),     rwm_exop_passwd },
978         { BER_BVNULL, NULL }
979 };
980
981 static int
982 rwm_extended( Operation *op, SlapReply *rs )
983 {
984         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
985         int                     rc;
986         rwm_op_cb *roc;
987
988         int     i;
989
990         for ( i = 0; exop_table[i].extended != NULL; i++ ) {
991                 if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
992                 {
993                         rc = exop_table[i].extended( op, rs );
994                         switch ( rc ) {
995                         case LDAP_SUCCESS:
996                                 break;
997
998                         case SLAP_CB_CONTINUE:
999                         case SLAPD_ABANDON:
1000                                 return rc;
1001
1002                         default:
1003                                 send_ldap_result( op, rs );
1004                                 return rc;
1005                         }
1006                         break;
1007                 }
1008         }
1009
1010         roc = rwm_callback_get( op, rs );
1011
1012 #ifdef ENABLE_REWRITE
1013         rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
1014 #else /* ! ENABLE_REWRITE */
1015         rc = 1;
1016         rc = rwm_op_dn_massage( op, rs, &rc, &roc->ros );
1017 #endif /* ! ENABLE_REWRITE */
1018         if ( rc != LDAP_SUCCESS ) {
1019                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1020                 send_ldap_error( op, rs, rc, "extendedDN massage error" );
1021                 return -1;
1022         }
1023
1024         /* TODO: rewrite/map extended data ? ... */
1025         op->o_callback = &roc->cb;
1026
1027         return SLAP_CB_CONTINUE;
1028 }
1029
1030 static int
1031 rwm_matched( Operation *op, SlapReply *rs )
1032 {
1033         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
1034         struct ldaprwmap        *rwmap = 
1035                         (struct ldaprwmap *)on->on_bi.bi_private;
1036
1037         struct berval           dn, mdn;
1038         dncookie                dc;
1039         int                     rc;
1040
1041         if ( rs->sr_matched == NULL ) {
1042                 return SLAP_CB_CONTINUE;
1043         }
1044
1045         dc.rwmap = rwmap;
1046 #ifdef ENABLE_REWRITE
1047         dc.conn = op->o_conn;
1048         dc.rs = rs;
1049         dc.ctx = "matchedDN";
1050 #else /* ! ENABLE_REWRITE */
1051         dc.tofrom = 0;
1052         dc.normalized = 0;
1053 #endif /* ! ENABLE_REWRITE */
1054         ber_str2bv( rs->sr_matched, 0, 0, &dn );
1055         mdn = dn;
1056         rc = rwm_dn_massage_pretty( &dc, &dn, &mdn );
1057         if ( rc != LDAP_SUCCESS ) {
1058                 rs->sr_err = rc;
1059                 rs->sr_text = "Rewrite error";
1060                 return 1;
1061         }
1062
1063         if ( mdn.bv_val != dn.bv_val ) {
1064                 if ( rs->sr_flags & REP_MATCHED_MUSTBEFREED ) {
1065                         ch_free( (void *)rs->sr_matched );
1066
1067                 } else {
1068                         rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
1069                 }
1070                 rs->sr_matched = mdn.bv_val;
1071         }
1072         
1073         return SLAP_CB_CONTINUE;
1074 }
1075
1076 static int
1077 rwm_attrs( Operation *op, SlapReply *rs, Attribute** a_first, int stripEntryDN )
1078 {
1079         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
1080         struct ldaprwmap        *rwmap = 
1081                         (struct ldaprwmap *)on->on_bi.bi_private;
1082
1083         dncookie                dc;
1084         int                     rc;
1085         Attribute               **ap;
1086         int                     isupdate;
1087
1088         /*
1089          * Rewrite the dn attrs, if needed
1090          */
1091         dc.rwmap = rwmap;
1092 #ifdef ENABLE_REWRITE
1093         dc.conn = op->o_conn;
1094         dc.rs = NULL; 
1095 #else /* ! ENABLE_REWRITE */
1096         dc.tofrom = 0;
1097         dc.normalized = 0;
1098 #endif /* ! ENABLE_REWRITE */
1099
1100         /* FIXME: the entries are in the remote mapping form;
1101          * so we need to select those attributes we are willing
1102          * to return, and remap them accordingly */
1103
1104         /* FIXME: in principle, one could map an attribute
1105          * on top of another, which already exists.
1106          * As such, in the end there might exist more than
1107          * one instance of an attribute.
1108          * We should at least check if this occurs, and issue
1109          * an error (because multiple instances of attrs in 
1110          * response are not valid), or merge the values (what
1111          * about duplicate values?) */
1112         isupdate = be_shadow_update( op );
1113         for ( ap = a_first; *ap; ) {
1114                 struct ldapmapping      *mapping = NULL;
1115                 int                     drop_missing;
1116                 int                     last;
1117                 Attribute               *a;
1118
1119                 if ( SLAP_OPATTRS( rs->sr_attr_flags ) && is_at_operational( (*ap)->a_desc->ad_type ) )
1120                 {
1121                         /* go on */ ;
1122                         
1123                 } else {
1124                         if ( op->ors_attrs != NULL && 
1125                                         !SLAP_USERATTRS( rs->sr_attr_flags ) &&
1126                                         !ad_inlist( (*ap)->a_desc, op->ors_attrs ) )
1127                         {
1128                                 goto cleanup_attr;
1129                         }
1130
1131                         drop_missing = rwm_mapping( &rwmap->rwm_at,
1132                                         &(*ap)->a_desc->ad_cname, &mapping, RWM_REMAP );
1133                         if ( drop_missing || ( mapping != NULL && BER_BVISEMPTY( &mapping->m_dst ) ) )
1134                         {
1135                                 goto cleanup_attr;
1136                         }
1137
1138                         if ( mapping != NULL ) {
1139                                 (*ap)->a_desc = mapping->m_dst_ad;
1140                         }
1141                 }
1142
1143                 if ( (*ap)->a_desc == slap_schema.si_ad_entryDN ) {
1144                         if ( stripEntryDN ) {
1145                                 /* will be generated by frontend */
1146                                 goto cleanup_attr;
1147                         }
1148                         
1149                 } else if ( !isupdate
1150                         && !get_relax( op )
1151                         && (*ap)->a_desc->ad_type->sat_no_user_mod 
1152                         && (*ap)->a_desc->ad_type != slap_schema.si_at_undefined )
1153                 {
1154                         goto next_attr;
1155                 }
1156
1157                 for ( last = 0; !BER_BVISNULL( &(*ap)->a_vals[last] ); last++ )
1158                         /* just count */ ;
1159
1160                 if ( last == 0 ) {
1161                         /* empty? leave it in place because of attrsonly and vlv */
1162                         goto next_attr;
1163                 }
1164                 last--;
1165
1166                 if ( (*ap)->a_desc == slap_schema.si_ad_objectClass
1167                                 || (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
1168                 {
1169                         struct berval   *bv;
1170                         
1171                         for ( bv = (*ap)->a_vals; !BER_BVISNULL( bv ); bv++ ) {
1172                                 struct berval   mapped;
1173
1174                                 rwm_map( &rwmap->rwm_oc, &bv[0], &mapped, RWM_REMAP );
1175                                 if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
1176                                         ch_free( bv[0].bv_val );
1177                                         BER_BVZERO( &bv[0] );
1178                                         if ( &(*ap)->a_vals[last] > &bv[0] ) {
1179                                                 bv[0] = (*ap)->a_vals[last];
1180                                                 BER_BVZERO( &(*ap)->a_vals[last] );
1181                                         }
1182                                         last--;
1183                                         bv--;
1184
1185                                 } else if ( mapped.bv_val != bv[0].bv_val ) {
1186                                         /*
1187                                          * FIXME: after LBER_FREEing
1188                                          * the value is replaced by
1189                                          * ch_alloc'ed memory
1190                                          */
1191                                         ber_bvreplace( &bv[0], &mapped );
1192                                 }
1193                         }
1194
1195                 /*
1196                  * It is necessary to try to rewrite attributes with
1197                  * dn syntax because they might be used in ACLs as
1198                  * members of groups; since ACLs are applied to the
1199                  * rewritten stuff, no dn-based subject clause could
1200                  * be used at the ldap backend side (see
1201                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
1202                  * The problem can be overcome by moving the dn-based
1203                  * ACLs to the target directory server, and letting
1204                  * everything pass thru the ldap backend. */
1205                 /* FIXME: handle distinguishedName-like syntaxes, like
1206                  * nameAndOptionalUID */
1207                 } else if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
1208                                 || ( mapping != NULL && mapping->m_src_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
1209                 {
1210 #ifdef ENABLE_REWRITE
1211                         dc.ctx = "searchAttrDN";
1212 #endif /* ENABLE_REWRITE */
1213                         rc = rwm_dnattr_result_rewrite( &dc, (*ap)->a_vals );
1214                         if ( rc != LDAP_SUCCESS ) {
1215                                 goto cleanup_attr;
1216                         }
1217
1218                 } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
1219 #ifdef ENABLE_REWRITE
1220                         dc.ctx = "searchAttrDN";
1221 #endif /* ENABLE_REWRITE */
1222                         rc = rwm_referral_result_rewrite( &dc, (*ap)->a_vals );
1223                         if ( rc != LDAP_SUCCESS ) {
1224                                 goto cleanup_attr;
1225                         }
1226                 }
1227
1228                 if ( mapping != NULL ) {
1229                         /* rewrite the attribute description */
1230                         assert( mapping->m_dst_ad != NULL );
1231                         (*ap)->a_desc = mapping->m_dst_ad;
1232                 }
1233
1234 next_attr:;
1235                 ap = &(*ap)->a_next;
1236                 continue;
1237
1238 cleanup_attr:;
1239                 a = *ap;
1240                 *ap = (*ap)->a_next;
1241
1242                 attr_free( a );
1243         }
1244
1245         return 0;
1246 }
1247
1248 static int
1249 rwm_send_entry( Operation *op, SlapReply *rs )
1250 {
1251         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
1252         struct ldaprwmap        *rwmap = 
1253                         (struct ldaprwmap *)on->on_bi.bi_private;
1254
1255         Entry                   *e = NULL;
1256         slap_mask_t             flags;
1257         struct berval           dn = BER_BVNULL,
1258                                 ndn = BER_BVNULL;
1259         dncookie                dc;
1260         int                     rc;
1261
1262         assert( rs->sr_entry != NULL );
1263
1264         /*
1265          * Rewrite the dn of the result, if needed
1266          */
1267         dc.rwmap = rwmap;
1268 #ifdef ENABLE_REWRITE
1269         dc.conn = op->o_conn;
1270         dc.rs = NULL; 
1271         dc.ctx = "searchEntryDN";
1272 #else /* ! ENABLE_REWRITE */
1273         dc.tofrom = 0;
1274         dc.normalized = 0;
1275 #endif /* ! ENABLE_REWRITE */
1276
1277         e = rs->sr_entry;
1278         flags = rs->sr_flags;
1279         if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
1280                 /* FIXME: all we need to duplicate are:
1281                  * - dn
1282                  * - ndn
1283                  * - attributes that are requested
1284                  * - no values if attrsonly is set
1285                  */
1286
1287                 e = entry_dup( e );
1288                 if ( e == NULL ) {
1289                         rc = LDAP_NO_MEMORY;
1290                         goto fail;
1291                 }
1292
1293                 flags &= ~REP_ENTRY_MUSTRELEASE;
1294                 flags |= ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
1295         }
1296
1297         /*
1298          * Note: this may fail if the target host(s) schema differs
1299          * from the one known to the meta, and a DN with unknown
1300          * attributes is returned.
1301          */
1302         dn = e->e_name;
1303         ndn = e->e_nname;
1304         rc = rwm_dn_massage_pretty_normalize( &dc, &e->e_name, &dn, &ndn );
1305         if ( rc != LDAP_SUCCESS ) {
1306                 rc = 1;
1307                 goto fail;
1308         }
1309
1310         if ( e->e_name.bv_val != dn.bv_val ) {
1311                 ch_free( e->e_name.bv_val );
1312                 ch_free( e->e_nname.bv_val );
1313
1314                 e->e_name = dn;
1315                 e->e_nname = ndn;
1316         }
1317
1318         /* TODO: map entry attribute types, objectclasses 
1319          * and dn-valued attribute values */
1320
1321         /* FIXME: the entries are in the remote mapping form;
1322          * so we need to select those attributes we are willing
1323          * to return, and remap them accordingly */
1324         (void)rwm_attrs( op, rs, &e->e_attrs, 1 );
1325
1326         if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
1327                 be_entry_release_rw( op, rs->sr_entry, 0 );
1328         }
1329
1330         rs->sr_entry = e;
1331         rs->sr_flags = flags;
1332
1333         return SLAP_CB_CONTINUE;
1334
1335 fail:;
1336         if ( e != NULL && e != rs->sr_entry ) {
1337                 if ( e->e_name.bv_val == dn.bv_val ) {
1338                         BER_BVZERO( &e->e_name );
1339                 }
1340
1341                 if ( e->e_nname.bv_val == ndn.bv_val ) {
1342                         BER_BVZERO( &e->e_nname );
1343                 }
1344
1345                 entry_free( e );
1346         }
1347
1348         if ( !BER_BVISNULL( &dn ) ) {
1349                 ch_free( dn.bv_val );
1350         }
1351
1352         if ( !BER_BVISNULL( &ndn ) ) {
1353                 ch_free( ndn.bv_val );
1354         }
1355
1356         return rc;
1357 }
1358
1359 static int
1360 rwm_operational( Operation *op, SlapReply *rs )
1361 {
1362         /* FIXME: the entries are in the remote mapping form;
1363          * so we need to select those attributes we are willing
1364          * to return, and remap them accordingly */
1365         if ( rs->sr_operational_attrs ) {
1366                 rwm_attrs( op, rs, &rs->sr_operational_attrs, 1 );
1367         }
1368
1369         return SLAP_CB_CONTINUE;
1370 }
1371
1372 #if 0
1373 /* don't use this; it cannot be reverted, and leaves op->o_req_dn
1374  * rewritten for subsequent operations; fine for plain suffixmassage,
1375  * but destroys everything else */
1376 static int
1377 rwm_chk_referrals( Operation *op, SlapReply *rs )
1378 {
1379         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
1380         int                     rc;
1381
1382 #ifdef ENABLE_REWRITE
1383         rc = rwm_op_dn_massage( op, rs, "referralCheckDN" );
1384 #else /* ! ENABLE_REWRITE */
1385         rc = 1;
1386         rc = rwm_op_dn_massage( op, rs, &rc );
1387 #endif /* ! ENABLE_REWRITE */
1388         if ( rc != LDAP_SUCCESS ) {
1389                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1390                 send_ldap_error( op, rs, rc, "referralCheckDN massage error" );
1391                 return -1;
1392         }
1393
1394         return SLAP_CB_CONTINUE;
1395 }
1396 #endif
1397
1398 static int
1399 rwm_rw_config(
1400         BackendDB       *be,
1401         const char      *fname,
1402         int             lineno,
1403         int             argc,
1404         char            **argv )
1405 {
1406 #ifdef ENABLE_REWRITE
1407         slap_overinst           *on = (slap_overinst *) be->bd_info;
1408         struct ldaprwmap        *rwmap = 
1409                         (struct ldaprwmap *)on->on_bi.bi_private;
1410
1411         return rewrite_parse( rwmap->rwm_rw,
1412                                 fname, lineno, argc, argv );
1413
1414 #else /* !ENABLE_REWRITE */
1415         fprintf( stderr, "%s: line %d: rewrite capabilities "
1416                         "are not enabled\n", fname, lineno );
1417 #endif /* !ENABLE_REWRITE */
1418                 
1419         return 0;
1420 }
1421
1422 static int
1423 rwm_suffixmassage_config(
1424         BackendDB       *be,
1425         const char      *fname,
1426         int             lineno,
1427         int             argc,
1428         char            **argv )
1429 {
1430         slap_overinst           *on = (slap_overinst *) be->bd_info;
1431         struct ldaprwmap        *rwmap = 
1432                         (struct ldaprwmap *)on->on_bi.bi_private;
1433
1434         struct berval           bvnc, nvnc, pvnc, brnc, nrnc, prnc;
1435         int                     massaged;
1436 #ifdef ENABLE_REWRITE
1437         int                     rc;
1438 #endif /* ENABLE_REWRITE */
1439                 
1440         /*
1441          * syntax:
1442          * 
1443          *      suffixmassage [<suffix>] <massaged suffix>
1444          *
1445          * the [<suffix>] field must be defined as a valid suffix
1446          * for the current database;
1447          * the <massaged suffix> shouldn't have already been
1448          * defined as a valid suffix for the current server
1449          */
1450         if ( argc == 2 ) {
1451                 if ( be->be_suffix == NULL ) {
1452                         fprintf( stderr, "%s: line %d: "
1453                                        " \"suffixMassage [<suffix>]"
1454                                        " <massaged suffix>\" without "
1455                                        "<suffix> part requires database "
1456                                        "suffix be defined first.\n",
1457                                 fname, lineno );
1458                         return 1;
1459                 }
1460                 bvnc = be->be_suffix[ 0 ];
1461                 massaged = 1;
1462
1463         } else if ( argc == 3 ) {
1464                 ber_str2bv( argv[ 1 ], 0, 0, &bvnc );
1465                 massaged = 2;
1466
1467         } else  {
1468                 fprintf( stderr, "%s: line %d: syntax is"
1469                                " \"suffixMassage [<suffix>]"
1470                                " <massaged suffix>\"\n",
1471                         fname, lineno );
1472                 return 1;
1473         }
1474
1475         if ( dnPrettyNormal( NULL, &bvnc, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
1476                 fprintf( stderr, "%s: line %d: suffix DN %s is invalid\n",
1477                         fname, lineno, bvnc.bv_val );
1478                 return 1;
1479         }
1480
1481         ber_str2bv( argv[ massaged ], 0, 0, &brnc );
1482         if ( dnPrettyNormal( NULL, &brnc, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
1483                 fprintf( stderr, "%s: line %d: suffix DN %s is invalid\n",
1484                                 fname, lineno, brnc.bv_val );
1485                 free( nvnc.bv_val );
1486                 free( pvnc.bv_val );
1487                 return 1;
1488         }
1489
1490 #ifdef ENABLE_REWRITE
1491         /*
1492          * The suffix massaging is emulated 
1493          * by means of the rewrite capabilities
1494          */
1495         rc = rwm_suffix_massage_config( rwmap->rwm_rw,
1496                         &pvnc, &nvnc, &prnc, &nrnc );
1497         free( nvnc.bv_val );
1498         free( pvnc.bv_val );
1499         free( nrnc.bv_val );
1500         free( prnc.bv_val );
1501
1502         return( rc );
1503
1504 #else /* !ENABLE_REWRITE */
1505         ber_bvarray_add( &rwmap->rwm_suffix_massage, &pvnc );
1506         ber_bvarray_add( &rwmap->rwm_suffix_massage, &nvnc );
1507                 
1508         ber_bvarray_add( &rwmap->rwm_suffix_massage, &prnc );
1509         ber_bvarray_add( &rwmap->rwm_suffix_massage, &nrnc );
1510 #endif /* !ENABLE_REWRITE */
1511
1512         return 0;
1513 }
1514
1515 static int
1516 rwm_m_config(
1517         BackendDB       *be,
1518         const char      *fname,
1519         int             lineno,
1520         int             argc,
1521         char            **argv )
1522 {
1523         slap_overinst           *on = (slap_overinst *) be->bd_info;
1524         struct ldaprwmap        *rwmap = 
1525                         (struct ldaprwmap *)on->on_bi.bi_private;
1526
1527         /* objectclass/attribute mapping */
1528         return rwm_map_config( &rwmap->rwm_oc,
1529                         &rwmap->rwm_at,
1530                         fname, lineno, argc, argv );
1531 }
1532
1533 static int
1534 rwm_response( Operation *op, SlapReply *rs )
1535 {
1536         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1537         struct ldaprwmap        *rwmap = 
1538                         (struct ldaprwmap *)on->on_bi.bi_private;
1539
1540         int             rc;
1541
1542         if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) {
1543                 return rwm_send_entry( op, rs );
1544         }
1545
1546         switch( op->o_tag ) {
1547         case LDAP_REQ_SEARCH:
1548         case LDAP_REQ_BIND:
1549         case LDAP_REQ_ADD:
1550         case LDAP_REQ_DELETE:
1551         case LDAP_REQ_MODRDN:
1552         case LDAP_REQ_MODIFY:
1553         case LDAP_REQ_COMPARE:
1554         case LDAP_REQ_EXTENDED:
1555                 if ( rs->sr_ref ) {
1556                         dncookie                dc;
1557
1558                         /*
1559                          * Rewrite the dn of the referrals, if needed
1560                          */
1561                         dc.rwmap = rwmap;
1562 #ifdef ENABLE_REWRITE
1563                         dc.conn = op->o_conn;
1564                         dc.rs = NULL; 
1565                         dc.ctx = "referralDN";
1566 #else /* ! ENABLE_REWRITE */
1567                         dc.tofrom = 0;
1568                         dc.normalized = 0;
1569 #endif /* ! ENABLE_REWRITE */
1570                         rc = rwm_referral_result_rewrite( &dc, rs->sr_ref );
1571                         if ( rc != LDAP_SUCCESS ) {
1572                                 rc = 1;
1573                                 break;
1574                         }
1575                 }
1576                 rc = rwm_matched( op, rs );
1577                 break;
1578
1579         default:
1580                 rc = SLAP_CB_CONTINUE;
1581                 break;
1582         }
1583
1584         return rc;
1585 }
1586
1587 static int
1588 rwm_db_config(
1589         BackendDB       *be,
1590         const char      *fname,
1591         int             lineno,
1592         int             argc,
1593         char            **argv )
1594 {
1595         slap_overinst           *on = (slap_overinst *) be->bd_info;
1596         struct ldaprwmap        *rwmap = 
1597                         (struct ldaprwmap *)on->on_bi.bi_private;
1598
1599         int             rc = 0;
1600         char            *argv0 = NULL;
1601
1602         if ( strncasecmp( argv[ 0 ], "rwm-", STRLENOF( "rwm-" ) ) == 0 ) {
1603                 argv0 = argv[ 0 ];
1604                 argv[ 0 ] = &argv0[ STRLENOF( "rwm-" ) ];
1605         }
1606
1607         if ( strncasecmp( argv[0], "rewrite", STRLENOF("rewrite") ) == 0 ) {
1608                 rc = rwm_rw_config( be, fname, lineno, argc, argv );
1609
1610         } else if ( strcasecmp( argv[0], "map" ) == 0 ) {
1611                 rc = rwm_m_config( be, fname, lineno, argc, argv );
1612
1613         } else if ( strcasecmp( argv[0], "suffixmassage" ) == 0 ) {
1614                 rc = rwm_suffixmassage_config( be, fname, lineno, argc, argv );
1615
1616         } else if ( strcasecmp( argv[0], "t-f-support" ) == 0 ) {
1617                 if ( argc != 2 ) {
1618                         fprintf( stderr,
1619                 "%s: line %d: \"t-f-support {no|yes|discover}\" needs 1 argument.\n",
1620                                         fname, lineno );
1621                         return( 1 );
1622                 }
1623
1624                 if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
1625                         rwmap->rwm_flags &= ~(RWM_F_SUPPORT_T_F|RWM_F_SUPPORT_T_F_DISCOVER);
1626
1627                 } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
1628                         rwmap->rwm_flags |= RWM_F_SUPPORT_T_F;
1629
1630                 /* TODO: not implemented yet */
1631                 } else if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
1632                         fprintf( stderr,
1633                 "%s: line %d: \"discover\" not supported yet "
1634                 "in \"t-f-support {no|yes|discover}\".\n",
1635                                         fname, lineno );
1636                         return( 1 );
1637 #if 0
1638                         rwmap->rwm_flags |= RWM_F_SUPPORT_T_F_DISCOVER;
1639 #endif
1640
1641                 } else {
1642                         fprintf( stderr,
1643         "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
1644                                 fname, lineno, argv[ 1 ] );
1645                         return 1;
1646                 }
1647
1648         } else {
1649                 rc = SLAP_CONF_UNKNOWN;
1650         }
1651
1652         if ( argv0 ) {
1653                 argv[ 0 ] = argv0;
1654         }
1655
1656         return rc;
1657 }
1658
1659 static int
1660 rwm_db_init(
1661         BackendDB       *be )
1662 {
1663         slap_overinst           *on = (slap_overinst *) be->bd_info;
1664         struct ldaprwmap        *rwmap;
1665 #ifdef ENABLE_REWRITE
1666         char                    *rargv[ 3 ];
1667 #endif /* ENABLE_REWRITE */
1668         int                     rc = 0;
1669
1670         rwmap = (struct ldaprwmap *)ch_calloc( 1, sizeof( struct ldaprwmap ) );
1671
1672 #ifdef ENABLE_REWRITE
1673         rwmap->rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1674         if ( rwmap->rwm_rw == NULL ) {
1675                 rc = -1;
1676                 goto error_return;
1677         }
1678
1679         /* this rewriteContext by default must be null;
1680          * rules can be added if required */
1681         rargv[ 0 ] = "rewriteContext";
1682         rargv[ 1 ] = "searchFilter";
1683         rargv[ 2 ] = NULL;
1684         rewrite_parse( rwmap->rwm_rw, "<suffix massage>", 1, 2, rargv );
1685
1686         rargv[ 0 ] = "rewriteContext";
1687         rargv[ 1 ] = "default";
1688         rargv[ 2 ] = NULL;
1689         rewrite_parse( rwmap->rwm_rw, "<suffix massage>", 2, 2, rargv );
1690 #endif /* ENABLE_REWRITE */
1691
1692 error_return:;
1693         on->on_bi.bi_private = (void *)rwmap;
1694
1695         if ( rc ) {
1696                 (void)rwm_db_destroy( be );
1697         }
1698
1699         return rc;
1700 }
1701
1702 static int
1703 rwm_db_destroy(
1704         BackendDB       *be )
1705 {
1706         slap_overinst   *on = (slap_overinst *) be->bd_info;
1707         int             rc = 0;
1708
1709         if ( on->on_bi.bi_private ) {
1710                 struct ldaprwmap        *rwmap = 
1711                         (struct ldaprwmap *)on->on_bi.bi_private;
1712
1713 #ifdef ENABLE_REWRITE
1714                 if ( rwmap->rwm_rw ) {
1715                         rewrite_info_delete( &rwmap->rwm_rw );
1716                 }
1717 #else /* !ENABLE_REWRITE */
1718                 if ( rwmap->rwm_suffix_massage ) {
1719                         ber_bvarray_free( rwmap->rwm_suffix_massage );
1720                 }
1721 #endif /* !ENABLE_REWRITE */
1722
1723                 avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
1724                 avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
1725                 avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
1726                 avl_free( rwmap->rwm_at.map, rwm_mapping_free );
1727
1728                 ch_free( rwmap );
1729         }
1730
1731         return rc;
1732 }
1733
1734 static slap_overinst rwm = { { NULL } };
1735
1736 #if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
1737 static
1738 #endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
1739 int
1740 rwm_initialize( void )
1741 {
1742         memset( &rwm, 0, sizeof( slap_overinst ) );
1743
1744         rwm.on_bi.bi_type = "rwm";
1745         rwm.on_bi.bi_flags =
1746                 SLAPO_BFLAG_SINGLE |
1747                 0;
1748
1749         rwm.on_bi.bi_db_init = rwm_db_init;
1750         rwm.on_bi.bi_db_config = rwm_db_config;
1751         rwm.on_bi.bi_db_destroy = rwm_db_destroy;
1752
1753         rwm.on_bi.bi_op_bind = rwm_op_bind;
1754         rwm.on_bi.bi_op_search = rwm_op_search;
1755         rwm.on_bi.bi_op_compare = rwm_op_compare;
1756         rwm.on_bi.bi_op_modify = rwm_op_modify;
1757         rwm.on_bi.bi_op_modrdn = rwm_op_modrdn;
1758         rwm.on_bi.bi_op_add = rwm_op_add;
1759         rwm.on_bi.bi_op_delete = rwm_op_delete;
1760         rwm.on_bi.bi_op_unbind = rwm_op_unbind;
1761         rwm.on_bi.bi_extended = rwm_extended;
1762
1763         rwm.on_bi.bi_operational = rwm_operational;
1764         rwm.on_bi.bi_chk_referrals = 0 /* rwm_chk_referrals */ ;
1765
1766 #ifdef ENABLE_REWRITE
1767         rwm.on_bi.bi_connection_init = rwm_conn_init;
1768         rwm.on_bi.bi_connection_destroy = rwm_conn_destroy;
1769 #endif /* ENABLE_REWRITE */
1770
1771         rwm.on_response = rwm_response;
1772
1773         return overlay_register( &rwm );
1774 }
1775
1776 #if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
1777 int
1778 init_module( int argc, char *argv[] )
1779 {
1780         return rwm_initialize();
1781 }
1782 #endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
1783
1784 #endif /* SLAPD_OVER_RWM */