]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
34c23de5118e2746e4acd20669fd0db4eac323d5
[openldap] / servers / slapd / back-meta / search.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
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 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29 #include <ac/time.h>
30
31 #include "slap.h"
32 #include "../back-ldap/back-ldap.h"
33 #include "back-meta.h"
34 #undef ldap_debug       /* silence a warning in ldap-int.h */
35 #include "ldap_log.h"
36 #include "../../../libraries/libldap/ldap-int.h"
37
38 static int
39 meta_send_entry(
40         Operation       *op,
41         SlapReply       *rs,
42         metaconn_t      *mc,
43         int             i,
44         LDAPMessage     *e );
45
46 static int
47 meta_back_search_start(
48         Operation               *op,
49         SlapReply               *rs,
50         dncookie                *dc,
51         metasingleconn_t        *msc,
52         int                     candidate,
53         SlapReply               *candidates
54 )
55 {
56         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
57         struct berval           realbase = op->o_req_dn;
58         int                     realscope = op->ors_scope;
59         ber_len_t               suffixlen = 0;
60         struct berval           mbase = BER_BVNULL; 
61         struct berval           mfilter = BER_BVNULL;
62         char                    **mapped_attrs = NULL;
63         int                     rc;
64
65         /* should we check return values? */
66         if ( op->ors_deref != -1 ) {
67                 ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
68                                 ( void * )&op->ors_deref);
69         }
70         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
71                 ldap_set_option( msc->msc_ld, LDAP_OPT_TIMELIMIT,
72                                 ( void * )&op->ors_tlimit);
73         }
74         if ( op->ors_slimit != SLAP_NO_LIMIT ) {
75                 ldap_set_option( msc->msc_ld, LDAP_OPT_SIZELIMIT,
76                                 ( void * )&op->ors_slimit);
77         }
78
79         dc->target = &mi->mi_targets[ candidate ];
80
81         /*
82          * modifies the base according to the scope, if required
83          */
84         suffixlen = mi->mi_targets[ candidate ].mt_nsuffix.bv_len;
85         if ( suffixlen > op->o_req_ndn.bv_len ) {
86                 switch ( op->ors_scope ) {
87                 case LDAP_SCOPE_SUBTREE:
88                         /*
89                          * make the target suffix the new base
90                          * FIXME: this is very forgiving, because
91                          * "illegal" searchBases may be turned
92                          * into the suffix of the target; however,
93                          * the requested searchBase already passed
94                          * thru the candidate analyzer...
95                          */
96                         if ( dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix,
97                                         &op->o_req_ndn ) )
98                         {
99                                 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
100
101                         } else {
102                                 /*
103                                  * this target is no longer candidate
104                                  */
105                                 return 0;
106                         }
107                         break;
108
109 #ifdef LDAP_SCOPE_SUBORDINATE
110                 case LDAP_SCOPE_SUBORDINATE:
111 #endif /* LDAP_SCOPE_SUBORDINATE */
112                 case LDAP_SCOPE_ONELEVEL:
113                 {
114                         struct berval   rdn = mi->mi_targets[ candidate ].mt_nsuffix;
115                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
116                         if ( dnIsOneLevelRDN( &rdn )
117                                         && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) )
118                         {
119                                 /*
120                                  * if there is exactly one level,
121                                  * make the target suffix the new
122                                  * base, and make scope "base"
123                                  */
124                                 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
125 #ifdef LDAP_SCOPE_SUBORDINATE
126                                 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
127                                         realscope = LDAP_SCOPE_SUBTREE;
128                                 } else
129 #endif /* LDAP_SCOPE_SUBORDINATE */
130                                 {
131                                         realscope = LDAP_SCOPE_BASE;
132                                 }
133                                 break;
134                         } /* else continue with the next case */
135                 }
136
137                 case LDAP_SCOPE_BASE:
138                         /*
139                          * this target is no longer candidate
140                          */
141                         return 0;
142                 }
143         }
144
145         /*
146          * Rewrite the search base, if required
147          */
148         dc->ctx = "searchBase";
149         switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
150         default:
151                 break;
152
153         case REWRITE_REGEXEC_UNWILLING:
154                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
155                 rs->sr_text = "Operation not allowed";
156                 send_ldap_result( op, rs );
157                 return -1;
158
159         case REWRITE_REGEXEC_ERR:
160
161                 /*
162                  * this target is no longer candidate
163                  */
164                 return 0;
165         }
166
167         /*
168          * Maps filter
169          */
170         rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
171                         &mfilter, BACKLDAP_MAP );
172         switch ( rc ) {
173         case LDAP_SUCCESS:
174                 break;
175
176         case LDAP_COMPARE_FALSE:
177         default:
178                 /*
179                  * this target is no longer candidate
180                  */
181                 rc = 0;
182                 goto done;
183         }
184
185         /*
186          * Maps required attributes
187          */
188         rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
189                         op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
190         if ( rc != LDAP_SUCCESS ) {
191                 /*
192                  * this target is no longer candidate
193                  */
194                 rc = 0;
195                 goto done;
196         }
197
198         /*
199          * Starts the search
200          */
201         rc = ldap_search_ext( msc->msc_ld,
202                         mbase.bv_val, realscope, mfilter.bv_val,
203                         mapped_attrs, op->ors_attrsonly,
204                         op->o_ctrls, NULL, NULL, op->ors_slimit,
205                         &candidates[ candidate ].sr_msgid ); 
206         if ( rc == LDAP_SUCCESS ) {
207                 rc = 1;
208
209         } else {
210                 candidates[ candidate ].sr_msgid = -1;
211                 rc = 0;
212         }
213
214 done:;
215         if ( mapped_attrs ) {
216                 free( mapped_attrs );
217         }
218         if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
219                 free( mfilter.bv_val );
220         }
221         if ( mbase.bv_val != realbase.bv_val ) {
222                 free( mbase.bv_val );
223         }
224
225         return rc;
226 }
227
228 int
229 meta_back_search( Operation *op, SlapReply *rs )
230 {
231         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
232         metaconn_t              *mc;
233         struct timeval          tv = { 0, 0 };
234         LDAPMessage             *res = NULL, *e;
235         int                     rc = 0, sres = LDAP_SUCCESS;
236         char                    *matched = NULL;
237         int                     i, last = 0, ncandidates = 0,
238                                 initial_candidates = 0, candidate_match = 0;
239         dncookie                dc;
240         int                     is_ok = 0;
241         void                    *savepriv;
242         SlapReply               *candidates = meta_back_candidates_get( op );
243
244         /*
245          * controls are set in ldap_back_dobind()
246          * 
247          * FIXME: in case of values return filter, we might want
248          * to map attrs and maybe rewrite value
249          */
250         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
251         if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
252                 return rs->sr_err;
253         }
254
255         dc.conn = op->o_conn;
256         dc.rs = rs;
257
258         /*
259          * Inits searches
260          */
261         for ( i = 0; i < mi->mi_ntargets; i++ ) {
262                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
263
264                 candidates[ i ].sr_msgid = -1;
265
266                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
267                         continue;
268                 }
269                 candidates[ i ].sr_err = LDAP_SUCCESS;
270                 candidates[ i ].sr_matched = NULL;
271                 candidates[ i ].sr_text = NULL;
272                 candidates[ i ].sr_ref = NULL;
273                 candidates[ i ].sr_ctrls = NULL;
274
275                 switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
276                 {
277                 case 0:
278                         break;
279
280                 case 1:
281                         ++ncandidates;
282                         break;
283
284                 case -1:
285                         rc = -1;
286                         goto finish;
287                 }
288         }
289
290         initial_candidates = ncandidates;
291
292 #if 0
293         {
294                 char    cnd[BUFSIZ];
295                 int     i;
296
297                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
298                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
299                                 cnd[ i ] = '*';
300                         } else {
301                                 cnd[ i ] = ' ';
302                         }
303                 }
304                 cnd[ i ] = '\0';
305
306                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
307                         "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
308         }
309 #endif
310
311         if ( initial_candidates == 0 ) {
312                 send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT, NULL );
313                 /* FIXME: find a way to look up the best match */
314
315                 rc = LDAP_NO_SUCH_OBJECT;
316                 goto finish;
317         }
318
319         /* We pull apart the ber result, stuff it into a slapd entry, and
320          * let send_search_entry stuff it back into ber format. Slow & ugly,
321          * but this is necessary for version matching, and for ACL processing.
322          */
323
324         /*
325          * In case there are no candidates, no cycle takes place...
326          *
327          * FIXME: we might use a queue, to better balance the load 
328          * among the candidates
329          */
330         for ( rc = 0; ncandidates > 0; ) {
331                 int     gotit = 0, doabandon = 0;
332
333                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
334                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
335
336                         if ( candidates[ i ].sr_msgid == -1 ) {
337                                 continue;
338                         }
339
340                         /* check for abandon */
341                         if ( op->o_abandon ) {
342                                 break;
343                         }
344                         
345                         /*
346                          * FIXME: handle time limit as well?
347                          * Note that target servers are likely 
348                          * to handle it, so at some time we'll
349                          * get a LDAP_TIMELIMIT_EXCEEDED from
350                          * one of them ...
351                          */
352                         rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
353                                         0, &tv, &res );
354
355                         if ( rc == 0 ) {
356                                 /* timeout exceeded */
357
358                                 /* FIXME: res should not need to be freed */
359                                 assert( res == NULL );
360
361                                 continue;
362
363                         } else if ( rc == -1 ) {
364 really_bad:;
365                                 /* something REALLY bad happened! */
366                                 ( void )meta_clear_unused_candidates( op, -1 );
367                                 rs->sr_err = LDAP_OTHER;
368                                 savepriv = op->o_private;
369                                 op->o_private = (void *)i;
370                                 send_ldap_result( op, rs );
371                                 op->o_private = savepriv;
372                                 
373                                 /* anything else needs be done? */
374
375                                 /* FIXME: res should not need to be freed */
376                                 assert( res == NULL );
377
378                                 goto finish;
379
380                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
381                                 if ( --op->ors_slimit == -1 ) {
382                                         ldap_msgfree( res );
383                                         res = NULL;
384
385                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
386                                         savepriv = op->o_private;
387                                         op->o_private = (void *)i;
388                                         send_ldap_result( op, rs );
389                                         op->o_private = savepriv;
390                                         goto finish;
391                                 }
392
393                                 is_ok++;
394
395                                 e = ldap_first_entry( msc->msc_ld, res );
396                                 savepriv = op->o_private;
397                                 op->o_private = (void *)i;
398                                 meta_send_entry( op, rs, mc, i, e );
399                                 op->o_private = savepriv;
400
401                                 ldap_msgfree( res );
402                                 res = NULL;
403
404                                 gotit = 1;
405
406 #if 0
407                                 /*
408                                  * If scope is BASE, we need to jump out
409                                  * as soon as one entry is found; if
410                                  * the target pool is properly crafted,
411                                  * this should correspond to the sole
412                                  * entry that has the base DN
413                                  */
414                                 /* FIXME: this defeats the purpose of
415                                  * doing a search with scope == base and
416                                  * sizelimit = 1 to determine if a
417                                  * candidate is actually unique */
418                                 if ( op->ors_scope == LDAP_SCOPE_BASE
419                                                 && rs->sr_nentries > 0 )
420                                 {
421                                         doabandon = 1;
422                                         ncandidates = 0;
423                                         sres = LDAP_SUCCESS;
424                                         break;
425                                 }
426 #endif
427
428                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
429                                 char            **references = NULL;
430                                 int             cnt;
431
432                                 is_ok++;
433
434                                 rc = ldap_parse_reference( msc->msc_ld, res,
435                                                 &references, &rs->sr_ctrls, 1 );
436                                 res = NULL;
437
438                                 if ( rc != LDAP_SUCCESS ) {
439                                         continue;
440                                 }
441
442                                 if ( references == NULL ) {
443                                         continue;
444                                 }
445
446 #ifdef ENABLE_REWRITE
447                                 dc.ctx = "referralDN";
448 #else /* ! ENABLE_REWRITE */
449                                 dc.tofrom = 0;
450                                 dc.normalized = 0;
451 #endif /* ! ENABLE_REWRITE */
452
453                                 /* FIXME: merge all and return at the end */
454
455                                 for ( cnt = 0; references[ cnt ]; cnt++ )
456                                         ;
457
458                                 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
459
460                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
461                                         ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
462                                 }
463                                 BER_BVZERO( &rs->sr_ref[ cnt ] );
464
465                                 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
466
467                                 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
468                                         /* ignore return value by now */
469                                         savepriv = op->o_private;
470                                         op->o_private = (void *)i;
471                                         ( void )send_search_reference( op, rs );
472                                         op->o_private = savepriv;
473
474                                         ber_bvarray_free( rs->sr_ref );
475                                         rs->sr_ref = NULL;
476                                 }
477
478                                 /* cleanup */
479                                 if ( references ) {
480                                         ldap_value_free( references );
481                                 }
482
483                                 if ( rs->sr_ctrls ) {
484                                         ldap_controls_free( rs->sr_ctrls );
485                                         rs->sr_ctrls = NULL;
486                                 }
487
488                         } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
489                                 char            buf[ SLAP_TEXT_BUFLEN ];
490                                 char            **references = NULL;
491
492                                 if ( ldap_parse_result( msc->msc_ld,
493                                                         res,
494                                                         &candidates[ i ].sr_err,
495                                                         (char **)&candidates[ i ].sr_matched,
496                                                         NULL /* (char **)&candidates[ i ].sr_text */ ,
497                                                         &references,
498                                                         &candidates[ i ].sr_ctrls, 1 ) )
499                                 {
500                                         res = NULL;
501                                         ldap_get_option( msc->msc_ld,
502                                                         LDAP_OPT_ERROR_NUMBER,
503                                                         &rs->sr_err );
504                                         sres = slap_map_api2result( rs );
505                                         goto really_bad;
506                                 }
507                                 rs->sr_err = candidates[ i ].sr_err;
508                                 sres = slap_map_api2result( rs );
509                                 res = NULL;
510
511                                 /* massage matchedDN if need be */
512                                 if ( candidates[ i ].sr_matched != NULL ) {
513                                         if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) {
514                                                 ldap_memfree( (char *)candidates[ i ].sr_matched );
515                                                 candidates[ i ].sr_matched = NULL;
516
517                                         } else {
518                                                 struct berval   match, mmatch;
519
520                                                 ber_str2bv( candidates[ i ].sr_matched,
521                                                         0, 0, &match );
522
523                                                 dc.ctx = "matchedDN";
524                                                 dc.target = &mi->mi_targets[ i ];
525
526                                                 if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
527                                                         if ( mmatch.bv_val == match.bv_val ) {
528                                                                 candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
529
530                                                         } else {
531                                                                 candidates[ i ].sr_matched = mmatch.bv_val;
532                                                         }
533
534                                                         candidate_match++;
535                                                 } 
536                                                 ldap_memfree( match.bv_val );
537                                         }
538                                 }
539
540                                 /* just get rid of the error message, if any */
541                                 if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' )
542                                 {
543                                         ldap_memfree( (char *)candidates[ i ].sr_text );
544                                         candidates[ i ].sr_text = NULL;
545                                 }
546
547                                 /* add references to array */
548                                 if ( references ) {
549                                         BerVarray       sr_ref;
550                                         int             cnt;
551
552                                         for ( cnt = 0; references[ cnt ]; cnt++ )
553                                                 ;
554
555                                         sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
556
557                                         for ( cnt = 0; references[ cnt ]; cnt++ ) {
558                                                 ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
559                                         }
560                                         BER_BVZERO( &sr_ref[ cnt ] );
561
562                                         ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
563                                 
564                                         /* cleanup */
565                                         ldap_value_free( references );
566
567                                         if ( rs->sr_v2ref == NULL ) {
568                                                 rs->sr_v2ref = sr_ref;
569
570                                         } else {
571                                                 for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
572                                                         ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
573                                                 }
574                                                 ber_memfree( sr_ref );
575                                         }
576                                 }
577
578                                 rs->sr_err = candidates[ i ].sr_err;
579                                 sres = slap_map_api2result( rs );
580                                 switch ( sres ) {
581                                 case LDAP_NO_SUCH_OBJECT:
582                                         /* is_ok is touched any time a valid
583                                          * (even intermediate) result is
584                                          * returned; as a consequence, if
585                                          * a candidate returns noSuchObject
586                                          * it is ignored and the candidate
587                                          * is simply demoted. */
588                                         if ( is_ok ) {
589                                                 sres = LDAP_SUCCESS;
590                                         }
591                                         break;
592
593                                 case LDAP_SUCCESS:
594                                 case LDAP_REFERRAL:
595                                         is_ok++;
596                                         break;
597                                 }
598
599                                 snprintf( buf, sizeof( buf ),
600                                         "%s meta_back_search[%d] "
601                                         "match=\"%s\" err=%d\n",
602                                         op->o_log_prefix, i,
603                                         candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
604                                         candidates[ i ].sr_err );
605                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
606
607                                 last = i;
608                                 rc = 0;
609
610                                 /*
611                                  * When no candidates are left,
612                                  * the outer cycle finishes
613                                  */
614                                 candidates[ i ].sr_msgid = -1;
615                                 --ncandidates;
616
617                         } else {
618                                 assert( 0 );
619                                 goto really_bad;
620                         }
621                 }
622
623                 /* check for abandon */
624                 if ( op->o_abandon || doabandon ) {
625                         for ( i = 0; i < mi->mi_ntargets; i++ ) {
626                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
627
628                                 if ( candidates[ i ].sr_msgid != -1 ) {
629                                         ldap_abandon_ext( msc->msc_ld,
630                                                 candidates[ i ].sr_msgid,
631                                                 NULL, NULL );
632                                         candidates[ i ].sr_msgid = -1;
633                                 }
634                         }
635
636                         if ( op->o_abandon ) {
637                                 rc = SLAPD_ABANDON;
638                                 goto finish;
639                         }
640                 }
641
642                 if ( gotit == 0 ) {
643                         tv.tv_sec = 0;
644                         tv.tv_usec = 100000;    /* 0.1 s */
645                         ldap_pvt_thread_yield();
646
647                 } else {
648                         tv.tv_sec = 0;
649                         tv.tv_usec = 0;
650                 }
651         }
652
653         if ( rc == -1 ) {
654                 /*
655                  * FIXME: need a better strategy to handle errors
656                  */
657                 rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
658                 goto finish;
659         }
660
661         /*
662          * Rewrite the matched portion of the search base, if required
663          * 
664          * FIXME: only the last one gets caught!
665          */
666         if ( candidate_match > 0 && rs->sr_nentries > 0 ) {
667                 /* we use the first one */
668                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
669                         if ( candidates[ i ].sr_tag == META_CANDIDATE
670                                         && candidates[ i ].sr_matched )
671                         {
672                                 matched = (char *)candidates[ i ].sr_matched;
673                                 candidates[ i ].sr_matched = NULL;
674                                 break;
675                         }
676                 }
677         }
678
679 #if 0
680         {
681                 char    buf[BUFSIZ];
682                 char    cnd[BUFSIZ];
683                 int     i;
684
685                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
686                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
687                                 cnd[ i ] = '*';
688                         } else {
689                                 cnd[ i ] = ' ';
690                         }
691                 }
692                 cnd[ i ] = '\0';
693
694                 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
695                         op->o_log_prefix, initial_candidates, is_ok, cnd );
696
697                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
698         }
699 #endif
700
701         /*
702          * In case we returned at least one entry, we return LDAP_SUCCESS
703          * otherwise, the latter error code we got
704          *
705          * FIXME: we should handle error codes and return the more 
706          * important/reasonable
707          */
708
709         if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
710                 sres = LDAP_REFERRAL;
711         }
712         rs->sr_err = sres;
713         rs->sr_matched = matched;
714         rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
715         savepriv = op->o_private;
716         op->o_private = (void *)mi->mi_ntargets;
717         send_ldap_result( op, rs );
718         op->o_private = savepriv;
719         rs->sr_matched = NULL;
720         rs->sr_ref = NULL;
721
722 finish:;
723         if ( matched ) {
724                 free( matched );
725         }
726
727         if ( rs->sr_v2ref ) {
728                 ber_bvarray_free( rs->sr_v2ref );
729         }
730
731         for ( i = 0; i < mi->mi_ntargets; i++ ) {
732                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
733                         continue;
734                 }
735
736                 if ( candidates[ i ].sr_matched ) {
737                         free( (char *)candidates[ i ].sr_matched );
738                         candidates[ i ].sr_matched = NULL;
739                 }
740
741                 if ( candidates[ i ].sr_text ) {
742                         ldap_memfree( (char *)candidates[ i ].sr_text );
743                         candidates[ i ].sr_text = NULL;
744                 }
745
746                 if ( candidates[ i ].sr_ref ) {
747                         ber_bvarray_free( candidates[ i ].sr_ref );
748                         candidates[ i ].sr_ref = NULL;
749                 }
750
751                 if ( candidates[ i ].sr_ctrls ) {
752                         ldap_controls_free( candidates[ i ].sr_ctrls );
753                         candidates[ i ].sr_ctrls = NULL;
754                 }
755         }
756
757         return rc;
758 }
759
760 static int
761 meta_send_entry(
762         Operation       *op,
763         SlapReply       *rs,
764         metaconn_t      *mc,
765         int             target,
766         LDAPMessage     *e )
767 {
768         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
769         struct berval           a, mapped;
770         Entry                   ent = { 0 };
771         BerElement              ber = *e->lm_ber;
772         Attribute               *attr, **attrp;
773         struct berval           *bv, bdn;
774         const char              *text;
775         dncookie                dc;
776
777         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
778                 return LDAP_DECODING_ERROR;
779         }
780
781         /*
782          * Rewrite the dn of the result, if needed
783          */
784         dc.target = &mi->mi_targets[ target ];
785         dc.conn = op->o_conn;
786         dc.rs = rs;
787         dc.ctx = "searchResult";
788
789         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
790         if ( rs->sr_err != LDAP_SUCCESS) {
791                 return rs->sr_err;
792         }
793
794         /*
795          * Note: this may fail if the target host(s) schema differs
796          * from the one known to the meta, and a DN with unknown
797          * attributes is returned.
798          * 
799          * FIXME: should we log anything, or delegate to dnNormalize?
800          */
801         if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
802                 op->o_tmpmemctx ) != LDAP_SUCCESS )
803         {
804                 return LDAP_INVALID_DN_SYNTAX;
805         }
806
807         /*
808          * cache dn
809          */
810         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
811                 ( void )meta_dncache_update_entry( &mi->mi_cache,
812                                 &ent.e_nname, target );
813         }
814
815         attrp = &ent.e_attrs;
816
817         dc.ctx = "searchAttrDN";
818         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
819                 int             last = 0;
820
821                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
822                                 &a, &mapped, BACKLDAP_REMAP );
823                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
824                         continue;
825                 }
826                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
827                 if ( attr == NULL ) {
828                         continue;
829                 }
830                 attr->a_flags = 0;
831                 attr->a_next = 0;
832                 attr->a_desc = NULL;
833                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
834                                 != LDAP_SUCCESS) {
835                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
836                                         != LDAP_SUCCESS )
837                         {
838                                 char    buf[ SLAP_TEXT_BUFLEN ];
839
840                                 snprintf( buf, sizeof( buf ),
841                                         "%s meta_send_entry(\"%s\"): "
842                                         "slap_bv2undef_ad(%s): %s\n",
843                                         op->o_log_prefix, ent.e_name.bv_val,
844                                         mapped.bv_val, text );
845
846                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
847                                 ch_free( attr );
848                                 continue;
849                         }
850                 }
851
852                 /* no subschemaSubentry */
853                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
854
855                         /* 
856                          * We eat target's subschemaSubentry because
857                          * a search for this value is likely not
858                          * to resolve to the appropriate backend;
859                          * later, the local subschemaSubentry is
860                          * added.
861                          */
862                         ( void )ber_scanf( &ber, "x" /* [W] */ );
863
864                         ch_free(attr);
865                         continue;
866                 }
867
868                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
869                                 || attr->a_vals == NULL )
870                 {
871                         attr->a_vals = (struct berval *)&slap_dummy_bv;
872
873                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
874                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
875                 {
876                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last );
877
878                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
879                                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
880                                                 bv, &mapped, BACKLDAP_REMAP );
881                                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
882                                         free( bv->bv_val );
883                                         BER_BVZERO( bv );
884                                         if ( --last < 0 ) {
885                                                 break;
886                                         }
887                                         *bv = attr->a_vals[ last ];
888                                         BER_BVZERO( &attr->a_vals[ last ] );
889                                         bv--;
890
891                                 } else if ( mapped.bv_val != bv->bv_val ) {
892                                         free( bv->bv_val );
893                                         ber_dupbv( bv, &mapped );
894                                 }
895                         }
896                 /*
897                  * It is necessary to try to rewrite attributes with
898                  * dn syntax because they might be used in ACLs as
899                  * members of groups; since ACLs are applied to the
900                  * rewritten stuff, no dn-based subecj clause could
901                  * be used at the ldap backend side (see
902                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
903                  * The problem can be overcome by moving the dn-based
904                  * ACLs to the target directory server, and letting
905                  * everything pass thru the ldap backend.
906                  */
907                 } else if ( attr->a_desc->ad_type->sat_syntax ==
908                                 slap_schema.si_syn_distinguishedName )
909                 {
910                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
911
912                 } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
913                         ldap_back_referral_result_rewrite( &dc, attr->a_vals );
914                 }
915
916                 if ( last && attr->a_desc->ad_type->sat_equality &&
917                         attr->a_desc->ad_type->sat_equality->smr_normalize ) {
918                         int i;
919
920                         attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
921                         for ( i = 0; i<last; i++ ) {
922                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
923                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
924                                         attr->a_desc->ad_type->sat_syntax,
925                                         attr->a_desc->ad_type->sat_equality,
926                                         &attr->a_vals[i], &attr->a_nvals[i],
927                                         NULL );
928                         }
929                         BER_BVZERO( &attr->a_nvals[i] );
930
931                 } else {
932                         attr->a_nvals = attr->a_vals;
933                 }
934
935                 *attrp = attr;
936                 attrp = &attr->a_next;
937         }
938         rs->sr_entry = &ent;
939         rs->sr_attrs = op->ors_attrs;
940         rs->sr_flags = 0;
941         send_search_entry( op, rs );
942         rs->sr_entry = NULL;
943         rs->sr_attrs = NULL;
944         
945         if ( !BER_BVISNULL( &ent.e_name ) && ent.e_name.bv_val != bdn.bv_val ) {
946                 free( ent.e_name.bv_val );
947                 BER_BVZERO( &ent.e_name );
948         }
949         if ( !BER_BVISNULL( &ent.e_nname ) ) {
950                 free( ent.e_nname.bv_val );
951                 BER_BVZERO( &ent.e_nname );
952         }
953         entry_clean( &ent );
954
955         return LDAP_SUCCESS;
956 }
957