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