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