]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
update for new backend types
[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
271                 candidates[ i ].sr_err = LDAP_SUCCESS;
272                 candidates[ i ].sr_matched = NULL;
273                 candidates[ i ].sr_text = NULL;
274                 candidates[ i ].sr_ref = NULL;
275                 candidates[ i ].sr_ctrls = NULL;
276
277                 switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
278                 {
279                 case 0:
280                         break;
281
282                 case 1:
283                         ++ncandidates;
284                         break;
285
286                 case -1:
287                         rc = -1;
288                         goto finish;
289                 }
290         }
291
292         initial_candidates = ncandidates;
293
294 #if 0
295         {
296                 char    cnd[BUFSIZ];
297                 int     i;
298
299                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
300                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
301                                 cnd[ i ] = '*';
302                         } else {
303                                 cnd[ i ] = ' ';
304                         }
305                 }
306                 cnd[ i ] = '\0';
307
308                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
309                         "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
310         }
311 #endif
312
313         if ( initial_candidates == 0 ) {
314                 send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT, NULL );
315                 /* FIXME: find a way to look up the best match */
316
317                 rc = LDAP_NO_SUCH_OBJECT;
318                 goto finish;
319         }
320
321         /* We pull apart the ber result, stuff it into a slapd entry, and
322          * let send_search_entry stuff it back into ber format. Slow & ugly,
323          * but this is necessary for version matching, and for ACL processing.
324          */
325
326         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
327                 stoptime = op->o_time + op->ors_tlimit;
328                 tv.tv_sec = 0;
329         }
330
331         /*
332          * In case there are no candidates, no cycle takes place...
333          *
334          * FIXME: we might use a queue, to better balance the load 
335          * among the candidates
336          */
337         for ( rc = 0; ncandidates > 0; ) {
338                 int     gotit = 0, doabandon = 0;
339
340                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
341                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
342
343                         if ( candidates[ i ].sr_msgid == -1 ) {
344                                 continue;
345                         }
346
347                         /* check for abandon */
348                         if ( op->o_abandon ) {
349                                 break;
350                         }
351                         
352                         /*
353                          * FIXME: handle time limit as well?
354                          * Note that target servers are likely 
355                          * to handle it, so at some time we'll
356                          * get a LDAP_TIMELIMIT_EXCEEDED from
357                          * one of them ...
358                          */
359                         rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
360                                         0, &tv, &res );
361
362                         if ( rc == 0 ) {
363                                 /* FIXME: res should not need to be freed */
364                                 assert( res == NULL );
365
366                                 /* check time limit */
367                                 if ( op->ors_tlimit != SLAP_NO_LIMIT
368                                                 && slap_get_time() > stoptime )
369                                 {
370                                         doabandon = 1;
371                                         rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
372                                         savepriv = op->o_private;
373                                         op->o_private = (void *)i;
374                                         send_ldap_result( op, rs );
375                                         op->o_private = savepriv;
376                                         goto finish;
377                                 }
378
379                                 continue;
380
381                         } else if ( rc == -1 ) {
382 really_bad:;
383                                 /* something REALLY bad happened! */
384                                 ( void )meta_clear_unused_candidates( op, -1 );
385                                 rs->sr_err = LDAP_OTHER;
386                                 savepriv = op->o_private;
387                                 op->o_private = (void *)i;
388                                 send_ldap_result( op, rs );
389                                 op->o_private = savepriv;
390                                 
391                                 /* anything else needs be done? */
392
393                                 /* FIXME: res should not need to be freed */
394                                 assert( res == NULL );
395
396                                 goto finish;
397
398                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
399                                 if ( --op->ors_slimit == -1 ) {
400                                         ldap_msgfree( res );
401                                         res = NULL;
402
403                                         rs->sr_err = LDAP_SIZELIMIT_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                                 is_ok++;
412
413                                 e = ldap_first_entry( msc->msc_ld, res );
414                                 savepriv = op->o_private;
415                                 op->o_private = (void *)i;
416                                 meta_send_entry( op, rs, mc, i, e );
417                                 op->o_private = savepriv;
418
419                                 ldap_msgfree( res );
420                                 res = NULL;
421
422                                 gotit = 1;
423
424 #if 0
425                                 /*
426                                  * If scope is BASE, we need to jump out
427                                  * as soon as one entry is found; if
428                                  * the target pool is properly crafted,
429                                  * this should correspond to the sole
430                                  * entry that has the base DN
431                                  */
432                                 /* FIXME: this defeats the purpose of
433                                  * doing a search with scope == base and
434                                  * sizelimit = 1 to determine if a
435                                  * candidate is actually unique */
436                                 if ( op->ors_scope == LDAP_SCOPE_BASE
437                                                 && rs->sr_nentries > 0 )
438                                 {
439                                         doabandon = 1;
440                                         ncandidates = 0;
441                                         sres = LDAP_SUCCESS;
442                                         break;
443                                 }
444 #endif
445
446                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
447                                 char            **references = NULL;
448                                 int             cnt;
449
450                                 is_ok++;
451
452                                 rc = ldap_parse_reference( msc->msc_ld, res,
453                                                 &references, &rs->sr_ctrls, 1 );
454                                 res = NULL;
455
456                                 if ( rc != LDAP_SUCCESS ) {
457                                         continue;
458                                 }
459
460                                 if ( references == NULL ) {
461                                         continue;
462                                 }
463
464 #ifdef ENABLE_REWRITE
465                                 dc.ctx = "referralDN";
466 #else /* ! ENABLE_REWRITE */
467                                 dc.tofrom = 0;
468                                 dc.normalized = 0;
469 #endif /* ! ENABLE_REWRITE */
470
471                                 /* FIXME: merge all and return at the end */
472
473                                 for ( cnt = 0; references[ cnt ]; cnt++ )
474                                         ;
475
476                                 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
477
478                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
479                                         ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
480                                 }
481                                 BER_BVZERO( &rs->sr_ref[ cnt ] );
482
483                                 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
484
485                                 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
486                                         /* ignore return value by now */
487                                         savepriv = op->o_private;
488                                         op->o_private = (void *)i;
489                                         ( void )send_search_reference( op, rs );
490                                         op->o_private = savepriv;
491
492                                         ber_bvarray_free( rs->sr_ref );
493                                         rs->sr_ref = NULL;
494                                 }
495
496                                 /* cleanup */
497                                 if ( references ) {
498                                         ldap_value_free( references );
499                                 }
500
501                                 if ( rs->sr_ctrls ) {
502                                         ldap_controls_free( rs->sr_ctrls );
503                                         rs->sr_ctrls = NULL;
504                                 }
505
506                         } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
507                                 char            buf[ SLAP_TEXT_BUFLEN ];
508                                 char            **references = NULL;
509
510                                 if ( ldap_parse_result( msc->msc_ld,
511                                                         res,
512                                                         &candidates[ i ].sr_err,
513                                                         (char **)&candidates[ i ].sr_matched,
514                                                         NULL /* (char **)&candidates[ i ].sr_text */ ,
515                                                         &references,
516                                                         &candidates[ i ].sr_ctrls, 1 ) != LDAP_SUCCESS )
517                                 {
518                                         res = NULL;
519                                         ldap_get_option( msc->msc_ld,
520                                                         LDAP_OPT_ERROR_NUMBER,
521                                                         &rs->sr_err );
522                                         sres = slap_map_api2result( rs );
523                                         goto really_bad;
524                                 }
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
600                                 snprintf( buf, sizeof( buf ),
601                                         "%s meta_back_search[%d] "
602                                         "match=\"%s\" err=%d\n",
603                                         op->o_log_prefix, i,
604                                         candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
605                                         candidates[ i ].sr_err );
606                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
607
608                                 switch ( sres ) {
609                                 case LDAP_NO_SUCH_OBJECT:
610                                         /* is_ok is touched any time a valid
611                                          * (even intermediate) result is
612                                          * returned; as a consequence, if
613                                          * a candidate returns noSuchObject
614                                          * it is ignored and the candidate
615                                          * is simply demoted. */
616                                         if ( is_ok ) {
617                                                 sres = LDAP_SUCCESS;
618                                         }
619                                         break;
620
621                                 case LDAP_SUCCESS:
622                                 case LDAP_REFERRAL:
623                                         is_ok++;
624                                         break;
625
626                                 default:
627                                         if ( META_BACK_ONERR_STOP( mi ) ) {
628                                                 savepriv = op->o_private;
629                                                 op->o_private = (void *)i;
630                                                 send_ldap_result( op, rs );
631                                                 op->o_private = savepriv;
632                                                 goto finish;
633                                         }
634                                         break;
635                                 }
636
637                                 last = i;
638                                 rc = 0;
639
640                                 /*
641                                  * When no candidates are left,
642                                  * the outer cycle finishes
643                                  */
644                                 candidates[ i ].sr_msgid = -1;
645                                 --ncandidates;
646
647                         } else {
648                                 assert( 0 );
649                                 goto really_bad;
650                         }
651                 }
652
653                 /* check for abandon */
654                 if ( op->o_abandon || doabandon ) {
655                         for ( i = 0; i < mi->mi_ntargets; i++ ) {
656                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
657
658                                 if ( candidates[ i ].sr_msgid != -1 ) {
659                                         ldap_abandon_ext( msc->msc_ld,
660                                                 candidates[ i ].sr_msgid,
661                                                 NULL, NULL );
662                                         candidates[ i ].sr_msgid = -1;
663                                 }
664                         }
665
666                         if ( op->o_abandon ) {
667                                 rc = SLAPD_ABANDON;
668                                 goto finish;
669                         }
670                 }
671
672                 if ( gotit == 0 ) {
673                         tv.tv_sec = 0;
674                         tv.tv_usec = 100000;    /* 0.1 s */
675                         ldap_pvt_thread_yield();
676
677                 } else {
678                         tv.tv_sec = 0;
679                         tv.tv_usec = 0;
680                 }
681         }
682
683         if ( rc == -1 ) {
684                 /*
685                  * FIXME: need a better strategy to handle errors
686                  */
687                 rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
688                 goto finish;
689         }
690
691         /*
692          * Rewrite the matched portion of the search base, if required
693          * 
694          * FIXME: only the last one gets caught!
695          */
696         if ( candidate_match > 0 && rs->sr_nentries > 0 ) {
697                 /* we use the first one */
698                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
699                         if ( candidates[ i ].sr_tag == META_CANDIDATE
700                                         && candidates[ i ].sr_matched )
701                         {
702                                 matched = (char *)candidates[ i ].sr_matched;
703                                 candidates[ i ].sr_matched = NULL;
704                                 break;
705                         }
706                 }
707         }
708
709 #if 0
710         {
711                 char    buf[BUFSIZ];
712                 char    cnd[BUFSIZ];
713                 int     i;
714
715                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
716                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
717                                 cnd[ i ] = '*';
718                         } else {
719                                 cnd[ i ] = ' ';
720                         }
721                 }
722                 cnd[ i ] = '\0';
723
724                 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
725                         op->o_log_prefix, initial_candidates, is_ok, cnd );
726
727                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
728         }
729 #endif
730
731         /*
732          * In case we returned at least one entry, we return LDAP_SUCCESS
733          * otherwise, the latter error code we got
734          *
735          * FIXME: we should handle error codes and return the more 
736          * important/reasonable
737          */
738
739         if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
740                 sres = LDAP_REFERRAL;
741         }
742         rs->sr_err = sres;
743         rs->sr_matched = matched;
744         rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
745         savepriv = op->o_private;
746         op->o_private = (void *)mi->mi_ntargets;
747         send_ldap_result( op, rs );
748         op->o_private = savepriv;
749         rs->sr_matched = NULL;
750         rs->sr_ref = NULL;
751
752 finish:;
753         if ( matched ) {
754                 free( matched );
755         }
756
757         if ( rs->sr_v2ref ) {
758                 ber_bvarray_free( rs->sr_v2ref );
759         }
760
761         for ( i = 0; i < mi->mi_ntargets; i++ ) {
762                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
763                         continue;
764                 }
765
766                 if ( candidates[ i ].sr_matched ) {
767                         free( (char *)candidates[ i ].sr_matched );
768                         candidates[ i ].sr_matched = NULL;
769                 }
770
771                 if ( candidates[ i ].sr_text ) {
772                         ldap_memfree( (char *)candidates[ i ].sr_text );
773                         candidates[ i ].sr_text = NULL;
774                 }
775
776                 if ( candidates[ i ].sr_ref ) {
777                         ber_bvarray_free( candidates[ i ].sr_ref );
778                         candidates[ i ].sr_ref = NULL;
779                 }
780
781                 if ( candidates[ i ].sr_ctrls ) {
782                         ldap_controls_free( candidates[ i ].sr_ctrls );
783                         candidates[ i ].sr_ctrls = NULL;
784                 }
785         }
786
787         meta_back_release_conn( op, mc );
788
789         return rc;
790 }
791
792 static int
793 meta_send_entry(
794         Operation       *op,
795         SlapReply       *rs,
796         metaconn_t      *mc,
797         int             target,
798         LDAPMessage     *e )
799 {
800         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
801         struct berval           a, mapped;
802         Entry                   ent = { 0 };
803         BerElement              ber = *e->lm_ber;
804         Attribute               *attr, **attrp;
805         struct berval           *bv, bdn;
806         const char              *text;
807         dncookie                dc;
808
809         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
810                 return LDAP_DECODING_ERROR;
811         }
812
813         /*
814          * Rewrite the dn of the result, if needed
815          */
816         dc.target = &mi->mi_targets[ target ];
817         dc.conn = op->o_conn;
818         dc.rs = rs;
819         dc.ctx = "searchResult";
820
821         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
822         if ( rs->sr_err != LDAP_SUCCESS) {
823                 return rs->sr_err;
824         }
825
826         /*
827          * Note: this may fail if the target host(s) schema differs
828          * from the one known to the meta, and a DN with unknown
829          * attributes is returned.
830          * 
831          * FIXME: should we log anything, or delegate to dnNormalize?
832          */
833         if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
834                 op->o_tmpmemctx ) != LDAP_SUCCESS )
835         {
836                 return LDAP_INVALID_DN_SYNTAX;
837         }
838
839         /*
840          * cache dn
841          */
842         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
843                 ( void )meta_dncache_update_entry( &mi->mi_cache,
844                                 &ent.e_nname, target );
845         }
846
847         attrp = &ent.e_attrs;
848
849         dc.ctx = "searchAttrDN";
850         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
851                 int             last = 0;
852
853                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
854                                 &a, &mapped, BACKLDAP_REMAP );
855                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
856                         continue;
857                 }
858                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
859                 if ( attr == NULL ) {
860                         continue;
861                 }
862                 attr->a_flags = 0;
863                 attr->a_next = 0;
864                 attr->a_desc = NULL;
865                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
866                                 != LDAP_SUCCESS) {
867                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
868                                         != LDAP_SUCCESS )
869                         {
870                                 char    buf[ SLAP_TEXT_BUFLEN ];
871
872                                 snprintf( buf, sizeof( buf ),
873                                         "%s meta_send_entry(\"%s\"): "
874                                         "slap_bv2undef_ad(%s): %s\n",
875                                         op->o_log_prefix, ent.e_name.bv_val,
876                                         mapped.bv_val, text );
877
878                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
879                                 ch_free( attr );
880                                 continue;
881                         }
882                 }
883
884                 /* no subschemaSubentry */
885                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
886
887                         /* 
888                          * We eat target's subschemaSubentry because
889                          * a search for this value is likely not
890                          * to resolve to the appropriate backend;
891                          * later, the local subschemaSubentry is
892                          * added.
893                          */
894                         ( void )ber_scanf( &ber, "x" /* [W] */ );
895
896                         ch_free(attr);
897                         continue;
898                 }
899
900                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
901                                 || attr->a_vals == NULL )
902                 {
903                         attr->a_vals = (struct berval *)&slap_dummy_bv;
904
905                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
906                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
907                 {
908                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last );
909
910                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
911                                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
912                                                 bv, &mapped, BACKLDAP_REMAP );
913                                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
914                                         free( bv->bv_val );
915                                         BER_BVZERO( bv );
916                                         if ( --last < 0 ) {
917                                                 break;
918                                         }
919                                         *bv = attr->a_vals[ last ];
920                                         BER_BVZERO( &attr->a_vals[ last ] );
921                                         bv--;
922
923                                 } else if ( mapped.bv_val != bv->bv_val ) {
924                                         free( bv->bv_val );
925                                         ber_dupbv( bv, &mapped );
926                                 }
927                         }
928                 /*
929                  * It is necessary to try to rewrite attributes with
930                  * dn syntax because they might be used in ACLs as
931                  * members of groups; since ACLs are applied to the
932                  * rewritten stuff, no dn-based subecj clause could
933                  * be used at the ldap backend side (see
934                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
935                  * The problem can be overcome by moving the dn-based
936                  * ACLs to the target directory server, and letting
937                  * everything pass thru the ldap backend.
938                  */
939                 } else if ( attr->a_desc->ad_type->sat_syntax ==
940                                 slap_schema.si_syn_distinguishedName )
941                 {
942                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
943
944                 } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
945                         ldap_back_referral_result_rewrite( &dc, attr->a_vals );
946                 }
947
948                 if ( last && attr->a_desc->ad_type->sat_equality &&
949                         attr->a_desc->ad_type->sat_equality->smr_normalize ) {
950                         int i;
951
952                         attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
953                         for ( i = 0; i<last; i++ ) {
954                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
955                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
956                                         attr->a_desc->ad_type->sat_syntax,
957                                         attr->a_desc->ad_type->sat_equality,
958                                         &attr->a_vals[i], &attr->a_nvals[i],
959                                         NULL );
960                         }
961                         BER_BVZERO( &attr->a_nvals[i] );
962
963                 } else {
964                         attr->a_nvals = attr->a_vals;
965                 }
966
967                 *attrp = attr;
968                 attrp = &attr->a_next;
969         }
970         rs->sr_entry = &ent;
971         rs->sr_attrs = op->ors_attrs;
972         rs->sr_flags = 0;
973         send_search_entry( op, rs );
974         rs->sr_entry = NULL;
975         rs->sr_attrs = NULL;
976         
977         if ( !BER_BVISNULL( &ent.e_name ) ) {
978                 if ( ent.e_name.bv_val != bdn.bv_val ) {
979                         free( ent.e_name.bv_val );
980                 }
981                 BER_BVZERO( &ent.e_name );
982         }
983         if ( !BER_BVISNULL( &ent.e_nname ) ) {
984                 free( ent.e_nname.bv_val );
985                 BER_BVZERO( &ent.e_nname );
986         }
987         entry_clean( &ent );
988
989         return LDAP_SUCCESS;
990 }
991