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