]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
ccd058c2a407377ab97627005558249a652ce9f8
[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 #ifdef LDAP_SCOPE_SUBORDINATE
101                                 if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
102                                         realscope = LDAP_SCOPE_SUBORDINATE;
103                                 }
104 #endif /* LDAP_SCOPE_SUBORDINATE */
105
106                         } else {
107                                 /*
108                                  * this target is no longer candidate
109                                  */
110                                 return 0;
111                         }
112                         break;
113
114 #ifdef LDAP_SCOPE_SUBORDINATE
115                 case LDAP_SCOPE_SUBORDINATE:
116 #endif /* LDAP_SCOPE_SUBORDINATE */
117                 case LDAP_SCOPE_ONELEVEL:
118                 {
119                         struct berval   rdn = mi->mi_targets[ candidate ].mt_nsuffix;
120                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
121                         if ( dnIsOneLevelRDN( &rdn )
122                                         && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) )
123                         {
124                                 /*
125                                  * if there is exactly one level,
126                                  * make the target suffix the new
127                                  * base, and make scope "base"
128                                  */
129                                 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
130 #ifdef LDAP_SCOPE_SUBORDINATE
131                                 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
132                                         if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
133                                                 realscope = LDAP_SCOPE_SUBORDINATE;
134                                         } else {
135                                                 realscope = LDAP_SCOPE_SUBTREE;
136                                         }
137                                 } else
138 #endif /* LDAP_SCOPE_SUBORDINATE */
139                                 {
140                                         realscope = LDAP_SCOPE_BASE;
141                                 }
142                                 break;
143                         } /* else continue with the next case */
144                 }
145
146                 case LDAP_SCOPE_BASE:
147                         /*
148                          * this target is no longer candidate
149                          */
150                         return 0;
151                 }
152         }
153
154         /*
155          * Rewrite the search base, if required
156          */
157         dc->ctx = "searchBase";
158         switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
159         default:
160                 break;
161
162         case REWRITE_REGEXEC_UNWILLING:
163                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
164                 rs->sr_text = "Operation not allowed";
165                 send_ldap_result( op, rs );
166                 return -1;
167
168         case REWRITE_REGEXEC_ERR:
169
170                 /*
171                  * this target is no longer candidate
172                  */
173                 return 0;
174         }
175
176         /*
177          * Maps filter
178          */
179         rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
180                         &mfilter, BACKLDAP_MAP );
181         switch ( rc ) {
182         case LDAP_SUCCESS:
183                 break;
184
185         case LDAP_COMPARE_FALSE:
186         default:
187                 /*
188                  * this target is no longer candidate
189                  */
190                 rc = 0;
191                 goto done;
192         }
193
194         /*
195          * Maps required attributes
196          */
197         rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
198                         op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
199         if ( rc != LDAP_SUCCESS ) {
200                 /*
201                  * this target is no longer candidate
202                  */
203                 rc = 0;
204                 goto done;
205         }
206
207         /*
208          * Starts the search
209          */
210         rc = ldap_search_ext( msc->msc_ld,
211                         mbase.bv_val, realscope, mfilter.bv_val,
212                         mapped_attrs, op->ors_attrsonly,
213                         op->o_ctrls, NULL, NULL, op->ors_slimit,
214                         &candidates[ candidate ].sr_msgid ); 
215         if ( rc == LDAP_SUCCESS ) {
216                 rc = 1;
217
218         } else {
219                 candidates[ candidate ].sr_msgid = -1;
220                 rc = 0;
221         }
222
223 done:;
224         if ( mapped_attrs ) {
225                 free( mapped_attrs );
226         }
227         if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
228                 free( mfilter.bv_val );
229         }
230         if ( mbase.bv_val != realbase.bv_val ) {
231                 free( mbase.bv_val );
232         }
233
234         return rc;
235 }
236
237 int
238 meta_back_search( Operation *op, SlapReply *rs )
239 {
240         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
241         metaconn_t      *mc;
242         struct timeval  tv = { 0, 0 };
243         time_t          stoptime;
244         LDAPMessage     *res = NULL, *e;
245         int             rc = 0, sres = LDAP_SUCCESS;
246         char            *matched = NULL;
247         int             last = 0, ncandidates = 0,
248                         initial_candidates = 0, candidate_match = 0;
249         long            i;
250         dncookie        dc;
251         int             is_ok = 0;
252         void            *savepriv;
253         SlapReply       *candidates = meta_back_candidates_get( op );
254
255         /*
256          * controls are set in ldap_back_dobind()
257          * 
258          * FIXME: in case of values return filter, we might want
259          * to map attrs and maybe rewrite value
260          */
261         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
262         if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
263                 return rs->sr_err;
264         }
265
266         dc.conn = op->o_conn;
267         dc.rs = rs;
268
269         /*
270          * Inits searches
271          */
272         for ( i = 0; i < mi->mi_ntargets; i++ ) {
273                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
274
275                 candidates[ i ].sr_msgid = -1;
276                 candidates[ i ].sr_matched = NULL;
277                 candidates[ i ].sr_text = NULL;
278                 candidates[ i ].sr_ref = NULL;
279                 candidates[ i ].sr_ctrls = NULL;
280
281                 if ( candidates[ i ].sr_tag != META_CANDIDATE
282                         || candidates[ i ].sr_err != LDAP_SUCCESS )
283                 {
284                         continue;
285                 }
286
287                 switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
288                 {
289                 case 0:
290                         break;
291
292                 case 1:
293                         ++ncandidates;
294                         break;
295
296                 case -1:
297                         rc = -1;
298                         goto finish;
299                 }
300         }
301
302         initial_candidates = ncandidates;
303
304 #if 0
305         {
306                 char    cnd[BUFSIZ];
307                 int     i;
308
309                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
310                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
311                                 cnd[ i ] = '*';
312                         } else {
313                                 cnd[ i ] = ' ';
314                         }
315                 }
316                 cnd[ i ] = '\0';
317
318                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
319                         "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
320         }
321 #endif
322
323         if ( initial_candidates == 0 ) {
324                 /* NOTE: here we are not sending any matchedDN;
325                  * this is intended, because if the back-meta
326                  * is serving this search request, but no valid
327                  * candidate could be looked up, it means that
328                  * there is a hole in the mapping of the targets
329                  * and thus no knowledge of any remote superior
330                  * is available */
331                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: "
332                         "base=\"%s\" scope=%d: "
333                         "no candidate could be selected\n",
334                         op->o_log_prefix, op->o_req_dn.bv_val,
335                         op->ors_scope );
336
337                 /* FIXME: we're sending the first error we encounter;
338                  * maybe we should pick the worst... */
339                 rc = LDAP_NO_SUCH_OBJECT;
340                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
341                         if ( candidates[ i ].sr_tag == META_CANDIDATE
342                                 && candidates[ i ].sr_err != LDAP_SUCCESS )
343                         {
344                                 rc = candidates[ i ].sr_err;
345                                 break;
346                         }
347                 }
348
349                 send_ldap_error( op, rs, rc, NULL );
350
351                 goto finish;
352         }
353
354         /* We pull apart the ber result, stuff it into a slapd entry, and
355          * let send_search_entry stuff it back into ber format. Slow & ugly,
356          * but this is necessary for version matching, and for ACL processing.
357          */
358
359         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
360                 stoptime = op->o_time + op->ors_tlimit;
361         }
362
363         /*
364          * In case there are no candidates, no cycle takes place...
365          *
366          * FIXME: we might use a queue, to better balance the load 
367          * among the candidates
368          */
369         for ( rc = 0; ncandidates > 0; ) {
370                 int     gotit = 0, doabandon = 0;
371
372                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
373                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
374
375                         if ( candidates[ i ].sr_msgid == -1 ) {
376                                 continue;
377                         }
378
379                         /* check for abandon */
380                         if ( op->o_abandon ) {
381                                 break;
382                         }
383                         
384                         /*
385                          * FIXME: handle time limit as well?
386                          * Note that target servers are likely 
387                          * to handle it, so at some time we'll
388                          * get a LDAP_TIMELIMIT_EXCEEDED from
389                          * one of them ...
390                          */
391                         rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
392                                         0, &tv, &res );
393
394                         if ( rc == 0 ) {
395                                 /* FIXME: res should not need to be freed */
396                                 assert( res == NULL );
397
398                                 /* check time limit */
399                                 if ( op->ors_tlimit != SLAP_NO_LIMIT
400                                                 && slap_get_time() > stoptime )
401                                 {
402                                         doabandon = 1;
403                                         rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
404                                         savepriv = op->o_private;
405                                         op->o_private = (void *)i;
406                                         send_ldap_result( op, rs );
407                                         op->o_private = savepriv;
408                                         goto finish;
409                                 }
410
411                                 continue;
412
413                         } else if ( rc == -1 ) {
414 really_bad:;
415                                 /* something REALLY bad happened! */
416                                 ( void )meta_clear_unused_candidates( op, -1 );
417                                 rs->sr_err = LDAP_OTHER;
418                                 savepriv = op->o_private;
419                                 op->o_private = (void *)i;
420                                 send_ldap_result( op, rs );
421                                 op->o_private = savepriv;
422                                 
423                                 /* anything else needs be done? */
424
425                                 /* FIXME: res should not need to be freed */
426                                 assert( res == NULL );
427
428                                 goto finish;
429
430                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
431                                 if ( --op->ors_slimit == -1 ) {
432                                         ldap_msgfree( res );
433                                         res = NULL;
434
435                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
436                                         savepriv = op->o_private;
437                                         op->o_private = (void *)i;
438                                         send_ldap_result( op, rs );
439                                         op->o_private = savepriv;
440                                         goto finish;
441                                 }
442
443                                 is_ok++;
444
445                                 e = ldap_first_entry( msc->msc_ld, res );
446                                 savepriv = op->o_private;
447                                 op->o_private = (void *)i;
448                                 meta_send_entry( op, rs, mc, i, e );
449                                 op->o_private = savepriv;
450
451                                 ldap_msgfree( res );
452                                 res = NULL;
453
454                                 gotit = 1;
455
456 #if 0
457                                 /*
458                                  * If scope is BASE, we need to jump out
459                                  * as soon as one entry is found; if
460                                  * the target pool is properly crafted,
461                                  * this should correspond to the sole
462                                  * entry that has the base DN
463                                  */
464                                 /* FIXME: this defeats the purpose of
465                                  * doing a search with scope == base and
466                                  * sizelimit = 1 to determine if a
467                                  * candidate is actually unique */
468                                 if ( op->ors_scope == LDAP_SCOPE_BASE
469                                                 && rs->sr_nentries > 0 )
470                                 {
471                                         doabandon = 1;
472                                         ncandidates = 0;
473                                         sres = LDAP_SUCCESS;
474                                         break;
475                                 }
476 #endif
477
478                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
479                                 char            **references = NULL;
480                                 int             cnt;
481
482                                 is_ok++;
483
484                                 rc = ldap_parse_reference( msc->msc_ld, res,
485                                                 &references, &rs->sr_ctrls, 1 );
486                                 res = NULL;
487
488                                 if ( rc != LDAP_SUCCESS ) {
489                                         continue;
490                                 }
491
492                                 if ( references == NULL ) {
493                                         continue;
494                                 }
495
496 #ifdef ENABLE_REWRITE
497                                 dc.ctx = "referralDN";
498 #else /* ! ENABLE_REWRITE */
499                                 dc.tofrom = 0;
500                                 dc.normalized = 0;
501 #endif /* ! ENABLE_REWRITE */
502
503                                 /* FIXME: merge all and return at the end */
504
505                                 for ( cnt = 0; references[ cnt ]; cnt++ )
506                                         ;
507
508                                 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
509
510                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
511                                         ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
512                                 }
513                                 BER_BVZERO( &rs->sr_ref[ cnt ] );
514
515                                 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
516
517                                 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
518                                         /* ignore return value by now */
519                                         savepriv = op->o_private;
520                                         op->o_private = (void *)i;
521                                         ( void )send_search_reference( op, rs );
522                                         op->o_private = savepriv;
523
524                                         ber_bvarray_free( rs->sr_ref );
525                                         rs->sr_ref = NULL;
526                                 }
527
528                                 /* cleanup */
529                                 if ( references ) {
530                                         ldap_value_free( references );
531                                 }
532
533                                 if ( rs->sr_ctrls ) {
534                                         ldap_controls_free( rs->sr_ctrls );
535                                         rs->sr_ctrls = NULL;
536                                 }
537
538                         } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
539                                 char            buf[ SLAP_TEXT_BUFLEN ];
540                                 char            **references = NULL;
541
542                                 if ( ldap_parse_result( msc->msc_ld,
543                                                         res,
544                                                         &candidates[ i ].sr_err,
545                                                         (char **)&candidates[ i ].sr_matched,
546                                                         NULL /* (char **)&candidates[ i ].sr_text */ ,
547                                                         &references,
548                                                         &candidates[ i ].sr_ctrls, 1 ) != LDAP_SUCCESS )
549                                 {
550                                         res = NULL;
551                                         ldap_get_option( msc->msc_ld,
552                                                         LDAP_OPT_ERROR_NUMBER,
553                                                         &rs->sr_err );
554                                         sres = slap_map_api2result( rs );
555                                         goto really_bad;
556                                 }
557
558                                 rs->sr_err = candidates[ i ].sr_err;
559                                 sres = slap_map_api2result( rs );
560                                 res = NULL;
561
562                                 /* massage matchedDN if need be */
563                                 if ( candidates[ i ].sr_matched != NULL ) {
564                                         if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) {
565                                                 ldap_memfree( (char *)candidates[ i ].sr_matched );
566                                                 candidates[ i ].sr_matched = NULL;
567
568                                         } else {
569                                                 struct berval   match, mmatch;
570
571                                                 ber_str2bv( candidates[ i ].sr_matched,
572                                                         0, 0, &match );
573
574                                                 dc.ctx = "matchedDN";
575                                                 dc.target = &mi->mi_targets[ i ];
576
577                                                 if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
578                                                         if ( mmatch.bv_val == match.bv_val ) {
579                                                                 candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
580
581                                                         } else {
582                                                                 candidates[ i ].sr_matched = mmatch.bv_val;
583                                                         }
584
585                                                         candidate_match++;
586                                                 } 
587                                                 ldap_memfree( match.bv_val );
588                                         }
589                                 }
590
591                                 /* just get rid of the error message, if any */
592                                 if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' )
593                                 {
594                                         ldap_memfree( (char *)candidates[ i ].sr_text );
595                                         candidates[ i ].sr_text = NULL;
596                                 }
597
598                                 /* add references to array */
599                                 if ( references ) {
600                                         BerVarray       sr_ref;
601                                         int             cnt;
602
603                                         for ( cnt = 0; references[ cnt ]; cnt++ )
604                                                 ;
605
606                                         sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
607
608                                         for ( cnt = 0; references[ cnt ]; cnt++ ) {
609                                                 ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
610                                         }
611                                         BER_BVZERO( &sr_ref[ cnt ] );
612
613                                         ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
614                                 
615                                         /* cleanup */
616                                         ldap_value_free( references );
617
618                                         if ( rs->sr_v2ref == NULL ) {
619                                                 rs->sr_v2ref = sr_ref;
620
621                                         } else {
622                                                 for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
623                                                         ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
624                                                 }
625                                                 ber_memfree( sr_ref );
626                                         }
627                                 }
628
629                                 rs->sr_err = candidates[ i ].sr_err;
630                                 sres = slap_map_api2result( rs );
631
632                                 snprintf( buf, sizeof( buf ),
633                                         "%s meta_back_search[%ld] "
634                                         "match=\"%s\" err=%ld\n",
635                                         op->o_log_prefix, i,
636                                         candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
637                                         (long) candidates[ i ].sr_err );
638                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
639
640                                 switch ( sres ) {
641                                 case LDAP_NO_SUCH_OBJECT:
642                                         /* is_ok is touched any time a valid
643                                          * (even intermediate) result is
644                                          * returned; as a consequence, if
645                                          * a candidate returns noSuchObject
646                                          * it is ignored and the candidate
647                                          * is simply demoted. */
648                                         if ( is_ok ) {
649                                                 sres = LDAP_SUCCESS;
650                                         }
651                                         break;
652
653                                 case LDAP_SUCCESS:
654                                 case LDAP_REFERRAL:
655                                         is_ok++;
656                                         break;
657
658                                 default:
659                                         if ( META_BACK_ONERR_STOP( mi ) ) {
660                                                 savepriv = op->o_private;
661                                                 op->o_private = (void *)i;
662                                                 send_ldap_result( op, rs );
663                                                 op->o_private = savepriv;
664                                                 goto finish;
665                                         }
666                                         break;
667                                 }
668
669                                 last = i;
670                                 rc = 0;
671
672                                 /*
673                                  * When no candidates are left,
674                                  * the outer cycle finishes
675                                  */
676                                 candidates[ i ].sr_msgid = -1;
677                                 --ncandidates;
678
679                         } else {
680                                 assert( 0 );
681                                 goto really_bad;
682                         }
683                 }
684
685                 /* check for abandon */
686                 if ( op->o_abandon || doabandon ) {
687                         for ( i = 0; i < mi->mi_ntargets; i++ ) {
688                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
689
690                                 if ( candidates[ i ].sr_msgid != -1 ) {
691                                         ldap_abandon_ext( msc->msc_ld,
692                                                 candidates[ i ].sr_msgid,
693                                                 NULL, NULL );
694                                         candidates[ i ].sr_msgid = -1;
695                                 }
696                         }
697
698                         if ( op->o_abandon ) {
699                                 rc = SLAPD_ABANDON;
700                                 goto finish;
701                         }
702                 }
703
704                 if ( gotit == 0 ) {
705                         LDAP_BACK_TV_SET( &tv );
706                         ldap_pvt_thread_yield();
707
708                 } else {
709                         tv.tv_sec = 0;
710                         tv.tv_usec = 0;
711                 }
712         }
713
714         if ( rc == -1 ) {
715                 /*
716                  * FIXME: need a better strategy to handle errors
717                  */
718                 rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
719                 goto finish;
720         }
721
722         /*
723          * Rewrite the matched portion of the search base, if required
724          * 
725          * FIXME: only the last one gets caught!
726          */
727         savepriv = op->o_private;
728         op->o_private = (void *)(long)mi->mi_ntargets;
729         if ( candidate_match > 0 ) {
730                 struct berval   pmatched = BER_BVNULL;
731
732                 /* we use the first one */
733                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
734                         if ( candidates[ i ].sr_tag == META_CANDIDATE
735                                         && candidates[ i ].sr_matched )
736                         {
737                                 struct berval   bv, pbv;
738                                 int             rc;
739
740                                 ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
741                                 rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
742
743                                 if ( rc == LDAP_SUCCESS ) {
744
745                                         /* NOTE: if they all are superiors
746                                          * of the baseDN, the shorter is also 
747                                          * superior of the longer... */
748                                         if ( pbv.bv_len > pmatched.bv_len ) {
749                                                 if ( !BER_BVISNULL( &pmatched ) ) {
750                                                         op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
751                                                 }
752                                                 pmatched = pbv;
753                                                 op->o_private = (void *)i;
754
755                                         } else {
756                                                 op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
757                                         }
758                                 }
759
760                                 if ( candidates[ i ].sr_matched != NULL ) {
761                                         free( (char *)candidates[ i ].sr_matched );
762                                         candidates[ i ].sr_matched = NULL;
763                                 }
764                         }
765                 }
766
767                 if ( !BER_BVISNULL( &pmatched ) ) {
768                         matched = pmatched.bv_val;
769                 }
770
771         } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
772                 matched = op->o_bd->be_suffix[ 0 ].bv_val;
773         }
774
775 #if 0
776         {
777                 char    buf[BUFSIZ];
778                 char    cnd[BUFSIZ];
779                 int     i;
780
781                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
782                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
783                                 cnd[ i ] = '*';
784                         } else {
785                                 cnd[ i ] = ' ';
786                         }
787                 }
788                 cnd[ i ] = '\0';
789
790                 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
791                         op->o_log_prefix, initial_candidates, is_ok, cnd );
792
793                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
794         }
795 #endif
796
797         /*
798          * In case we returned at least one entry, we return LDAP_SUCCESS
799          * otherwise, the latter error code we got
800          *
801          * FIXME: we should handle error codes and return the more 
802          * important/reasonable
803          */
804
805         if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
806                 sres = LDAP_REFERRAL;
807         }
808         rs->sr_err = sres;
809         rs->sr_matched = matched;
810         rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
811         send_ldap_result( op, rs );
812         op->o_private = savepriv;
813         rs->sr_matched = NULL;
814         rs->sr_ref = NULL;
815
816 finish:;
817         if ( matched && matched != op->o_bd->be_suffix[ 0 ].bv_val ) {
818                 op->o_tmpfree( matched, op->o_tmpmemctx );
819         }
820
821         if ( rs->sr_v2ref ) {
822                 ber_bvarray_free( rs->sr_v2ref );
823         }
824
825         for ( i = 0; i < mi->mi_ntargets; i++ ) {
826                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
827                         continue;
828                 }
829
830                 if ( candidates[ i ].sr_matched ) {
831                         free( (char *)candidates[ i ].sr_matched );
832                         candidates[ i ].sr_matched = NULL;
833                 }
834
835                 if ( candidates[ i ].sr_text ) {
836                         ldap_memfree( (char *)candidates[ i ].sr_text );
837                         candidates[ i ].sr_text = NULL;
838                 }
839
840                 if ( candidates[ i ].sr_ref ) {
841                         ber_bvarray_free( candidates[ i ].sr_ref );
842                         candidates[ i ].sr_ref = NULL;
843                 }
844
845                 if ( candidates[ i ].sr_ctrls ) {
846                         ldap_controls_free( candidates[ i ].sr_ctrls );
847                         candidates[ i ].sr_ctrls = NULL;
848                 }
849         }
850
851         meta_back_release_conn( op, mc );
852
853         return rs->sr_err;
854 }
855
856 static int
857 meta_send_entry(
858         Operation       *op,
859         SlapReply       *rs,
860         metaconn_t      *mc,
861         int             target,
862         LDAPMessage     *e )
863 {
864         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
865         struct berval           a, mapped;
866         Entry                   ent = { 0 };
867         BerElement              ber = *e->lm_ber;
868         Attribute               *attr, **attrp;
869         struct berval           bdn,
870                                 dn = BER_BVNULL;
871         const char              *text;
872         dncookie                dc;
873         int                     rc;
874
875         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
876                 return LDAP_DECODING_ERROR;
877         }
878
879         /*
880          * Rewrite the dn of the result, if needed
881          */
882         dc.target = &mi->mi_targets[ target ];
883         dc.conn = op->o_conn;
884         dc.rs = rs;
885         dc.ctx = "searchResult";
886
887         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &dn );
888         if ( rs->sr_err != LDAP_SUCCESS) {
889                 return rs->sr_err;
890         }
891
892         /*
893          * Note: this may fail if the target host(s) schema differs
894          * from the one known to the meta, and a DN with unknown
895          * attributes is returned.
896          * 
897          * FIXME: should we log anything, or delegate to dnNormalize?
898          */
899         rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
900                 op->o_tmpmemctx );
901         if ( dn.bv_val != bdn.bv_val ) {
902                 free( dn.bv_val );
903         }
904         BER_BVZERO( &dn );
905
906         if ( rc != LDAP_SUCCESS ) {
907                 return LDAP_INVALID_DN_SYNTAX;
908         }
909
910         /*
911          * cache dn
912          */
913         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
914                 ( void )meta_dncache_update_entry( &mi->mi_cache,
915                                 &ent.e_nname, target );
916         }
917
918         attrp = &ent.e_attrs;
919
920         dc.ctx = "searchAttrDN";
921         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
922                 int                             last = 0;
923                 slap_syntax_validate_func       *validate;
924                 slap_syntax_transform_func      *pretty;
925
926                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
927                                 &a, &mapped, BACKLDAP_REMAP );
928                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
929                         ( void )ber_scanf( &ber, "x" /* [W] */ );
930                         continue;
931                 }
932                 attr = ( Attribute * )ch_calloc( 1, sizeof( Attribute ) );
933                 if ( attr == NULL ) {
934                         continue;
935                 }
936                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
937                                 != LDAP_SUCCESS) {
938                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text,
939                                 SLAP_AD_PROXIED ) != LDAP_SUCCESS )
940                         {
941                                 char    buf[ SLAP_TEXT_BUFLEN ];
942
943                                 snprintf( buf, sizeof( buf ),
944                                         "%s meta_send_entry(\"%s\"): "
945                                         "slap_bv2undef_ad(%s): %s\n",
946                                         op->o_log_prefix, ent.e_name.bv_val,
947                                         mapped.bv_val, text );
948
949                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
950                                 ch_free( attr );
951                                 continue;
952                         }
953                 }
954
955                 /* no subschemaSubentry */
956                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
957                         || attr->a_desc == slap_schema.si_ad_entryDN )
958                 {
959
960                         /* 
961                          * We eat target's subschemaSubentry because
962                          * a search for this value is likely not
963                          * to resolve to the appropriate backend;
964                          * later, the local subschemaSubentry is
965                          * added.
966                          *
967                          * We also eat entryDN because the frontend
968                          * will reattach it without checking if already
969                          * present...
970                          */
971                         ( void )ber_scanf( &ber, "x" /* [W] */ );
972
973                         ch_free(attr);
974                         continue;
975                 }
976
977                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
978                                 || attr->a_vals == NULL )
979                 {
980                         attr->a_vals = (struct berval *)&slap_dummy_bv;
981
982                 } else {
983                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
984                                 ;
985                 }
986
987                 validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
988                 pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
989
990                 if ( !validate && !pretty ) {
991                         attr_free( attr );
992                         goto next_attr;
993                 }
994
995                 if ( attr->a_desc == slap_schema.si_ad_objectClass
996                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
997                 {
998                         struct berval   *bv;
999
1000                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
1001                                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
1002                                                 bv, &mapped, BACKLDAP_REMAP );
1003                                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
1004                                         free( bv->bv_val );
1005                                         BER_BVZERO( bv );
1006                                         if ( --last < 0 ) {
1007                                                 break;
1008                                         }
1009                                         *bv = attr->a_vals[ last ];
1010                                         BER_BVZERO( &attr->a_vals[ last ] );
1011                                         bv--;
1012
1013                                 } else if ( mapped.bv_val != bv->bv_val ) {
1014                                         free( bv->bv_val );
1015                                         ber_dupbv( bv, &mapped );
1016                                 }
1017                         }
1018                 /*
1019                  * It is necessary to try to rewrite attributes with
1020                  * dn syntax because they might be used in ACLs as
1021                  * members of groups; since ACLs are applied to the
1022                  * rewritten stuff, no dn-based subecj clause could
1023                  * be used at the ldap backend side (see
1024                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
1025                  * The problem can be overcome by moving the dn-based
1026                  * ACLs to the target directory server, and letting
1027                  * everything pass thru the ldap backend.
1028                  */
1029                 } else {
1030                         int     i;
1031
1032                         if ( attr->a_desc->ad_type->sat_syntax ==
1033                                 slap_schema.si_syn_distinguishedName )
1034                         {
1035                                 ldap_dnattr_result_rewrite( &dc, attr->a_vals );
1036
1037                         } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
1038                                 ldap_back_referral_result_rewrite( &dc, attr->a_vals );
1039
1040                         }
1041
1042                         for ( i = 0; i < last; i++ ) {
1043                                 struct berval   pval;
1044                                 int             rc;
1045
1046                                 if ( pretty ) {
1047                                         rc = pretty( attr->a_desc->ad_type->sat_syntax,
1048                                                 &attr->a_vals[i], &pval, NULL );
1049
1050                                 } else {
1051                                         rc = validate( attr->a_desc->ad_type->sat_syntax,
1052                                                 &attr->a_vals[i] );
1053                                 }
1054
1055                                 if ( rc ) {
1056                                         LBER_FREE( attr->a_vals[i].bv_val );
1057                                         if ( --last == i ) {
1058                                                 BER_BVZERO( &attr->a_vals[ i ] );
1059                                                 break;
1060                                         }
1061                                         attr->a_vals[i] = attr->a_vals[last];
1062                                         BER_BVZERO( &attr->a_vals[last] );
1063                                         i--;
1064                                         continue;
1065                                 }
1066
1067                                 if ( pretty ) {
1068                                         LBER_FREE( attr->a_vals[i].bv_val );
1069                                         attr->a_vals[i] = pval;
1070                                 }
1071                         }
1072
1073                         if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
1074                                 attr_free( attr );
1075                                 goto next_attr;
1076                         }
1077                 }
1078
1079                 if ( last && attr->a_desc->ad_type->sat_equality &&
1080                         attr->a_desc->ad_type->sat_equality->smr_normalize )
1081                 {
1082                         int i;
1083
1084                         attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
1085                         for ( i = 0; i<last; i++ ) {
1086                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
1087                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
1088                                         attr->a_desc->ad_type->sat_syntax,
1089                                         attr->a_desc->ad_type->sat_equality,
1090                                         &attr->a_vals[i], &attr->a_nvals[i],
1091                                         NULL );
1092                         }
1093                         BER_BVZERO( &attr->a_nvals[i] );
1094
1095                 } else {
1096                         attr->a_nvals = attr->a_vals;
1097                 }
1098
1099                 *attrp = attr;
1100                 attrp = &attr->a_next;
1101 next_attr:;
1102         }
1103         rs->sr_entry = &ent;
1104         rs->sr_attrs = op->ors_attrs;
1105         rs->sr_flags = 0;
1106         send_search_entry( op, rs );
1107         rs->sr_entry = NULL;
1108         rs->sr_attrs = NULL;
1109         
1110         if ( !BER_BVISNULL( &ent.e_name ) ) {
1111                 free( ent.e_name.bv_val );
1112                 BER_BVZERO( &ent.e_name );
1113         }
1114         if ( !BER_BVISNULL( &ent.e_nname ) ) {
1115                 free( ent.e_nname.bv_val );
1116                 BER_BVZERO( &ent.e_nname );
1117         }
1118         entry_clean( &ent );
1119
1120         return LDAP_SUCCESS;
1121 }
1122