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