]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
misc cleanup
[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-2003 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENTS:
16  * This work was initially developed by the Howard Chu for inclusion
17  * in OpenLDAP Software and subsequently enhanced by Pierangelo
18  * Masarati.
19  */
20 /* This is an altered version */
21 /*
22  * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
23  *
24  * This work has been developed to fulfill the requirements
25  * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
26  * to the OpenLDAP Foundation in the hope that it may be useful
27  * to the Open Source community, but WITHOUT ANY WARRANTY.
28  *
29  * Permission is granted to anyone to use this software for any purpose
30  * on any computer system, and to alter it and redistribute it, subject
31  * to the following restrictions:
32  *
33  * 1. The author and SysNet s.n.c. are not responsible for the consequences
34  *    of use of this software, no matter how awful, even if they arise from 
35  *    flaws in it.
36  *
37  * 2. The origin of this software must not be misrepresented, either by
38  *    explicit claim or by omission.  Since few users ever read sources,
39  *    credits should appear in the documentation.
40  *
41  * 3. Altered versions must be plainly marked as such, and must not be
42  *    misrepresented as being the original software.  Since few users
43  *    ever read sources, credits should appear in the documentation.
44  *    SysNet s.n.c. cannot be responsible for the consequences of the
45  *    alterations.
46  *
47  * 4. This notice may not be removed or altered.
48  *
49  *
50  * This software is based on the backend back-ldap, implemented
51  * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
52  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
53  * contributors. The contribution of the original software to the present
54  * implementation is acknowledged in this copyright statement.
55  *
56  * A special acknowledgement goes to Howard for the overall architecture
57  * (and for borrowing large pieces of code), and to Mark, who implemented
58  * from scratch the attribute/objectclass mapping.
59  *
60  * The original copyright statement follows.
61  *
62  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
63  *
64  * Permission is granted to anyone to use this software for any purpose
65  * on any computer system, and to alter it and redistribute it, subject
66  * to the following restrictions:
67  *
68  * 1. The author is not responsible for the consequences of use of this
69  *    software, no matter how awful, even if they arise from flaws in it.
70  *
71  * 2. The origin of this software must not be misrepresented, either by
72  *    explicit claim or by omission.  Since few users ever read sources,
73  *    credits should appear in the documentation.
74  *
75  * 3. Altered versions must be plainly marked as such, and must not be
76  *    misrepresented as being the original software.  Since few users
77  *    ever read sources, credits should appear in the
78  *    documentation.
79  *
80  * 4. This notice may not be removed or altered.
81  *                
82  */
83
84 #include "portable.h"
85
86 #include <stdio.h>
87
88 #include <ac/socket.h>
89 #include <ac/string.h>
90 #include <ac/time.h>
91
92 #include "slap.h"
93 #include "../back-ldap/back-ldap.h"
94 #include "back-meta.h"
95 #include "ldap_pvt.h"
96 #undef ldap_debug       /* silence a warning in ldap-int.h */
97 #include "ldap_log.h"
98 #include "../../../libraries/libldap/ldap-int.h"
99
100 static int
101 meta_send_entry(
102                 Operation       *op,
103                 SlapReply       *rs,
104                 struct metaconn *lc,
105                 int             i,
106                 LDAPMessage     *e
107 );
108
109 static int
110 is_one_level_rdn(
111                 const char      *rdn,
112                 int             from
113 );
114
115 int
116 meta_back_search( Operation *op, SlapReply *rs )
117 {
118         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
119         struct metaconn *lc;
120         struct metasingleconn *lsc;
121         struct timeval  tv = { 0, 0 };
122         LDAPMessage     *res, *e;
123         int     rc = 0, *msgid, sres = LDAP_NO_SUCH_OBJECT;
124         char *err = NULL;
125         struct berval match = { 0, NULL }, mmatch = { 0, NULL };
126         BerVarray v2refs = NULL;
127                 
128         int i, last = 0, candidates = 0, initial_candidates = 0,
129                         candidate_match = 0;
130         struct slap_limits_set *limit = NULL;
131         int isroot = 0;
132         dncookie dc;
133
134         cache_manager*  cm = li->cm;
135
136         if (cm->caching) {
137                 return meta_back_cache_search(op, rs);
138         }
139         
140         /*
141          * controls are set in ldap_back_dobind()
142          * 
143          * FIXME: in case of values return filter, we might want
144          * to map attrs and maybe rewrite value
145          */
146         lc = meta_back_getconn( op, rs, META_OP_ALLOW_MULTIPLE, 
147                         &op->o_req_ndn, NULL );
148         if ( !lc ) {
149                 send_ldap_result( op, rs );
150                 return -1;
151         }
152
153         if ( !meta_back_dobind( lc, op ) ) {
154                 rs->sr_err = LDAP_OTHER;
155                 send_ldap_result( op, rs );
156                 return -1;
157         }
158
159         /*
160          * Array of message id of each target
161          */
162         msgid = ch_calloc( sizeof( int ), li->ntargets );
163         if ( msgid == NULL ) {
164                 rs->sr_err = LDAP_OTHER;
165                 send_ldap_result( op, rs );
166                 return -1;
167         }
168         
169         /* if not root, get appropriate limits */
170         if ( be_isroot( op->o_bd, &op->o_ndn ) ) {
171                 isroot = 1;
172         } else {
173                 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
174         }
175
176         /* if no time limit requested, rely on remote server limits */
177         /* if requested limit higher than hard limit, abort */
178         if ( !isroot && op->oq_search.rs_tlimit > limit->lms_t_hard ) {
179                 /* no hard limit means use soft instead */
180                 if ( limit->lms_t_hard == 0
181                                 && limit->lms_t_soft > -1
182                                 && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
183                         op->oq_search.rs_tlimit = limit->lms_t_soft;
184                         
185                 /* positive hard limit means abort */
186                 } else if ( limit->lms_t_hard > 0 ) {
187                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
188                         send_ldap_result( op, rs );
189                         rc = 0;
190                         goto finish;
191                 }
192                 
193                 /* negative hard limit means no limit */
194         }
195         
196         /* if no size limit requested, rely on remote server limits */
197         /* if requested limit higher than hard limit, abort */
198         if ( !isroot && op->oq_search.rs_slimit > limit->lms_s_hard ) {
199                 /* no hard limit means use soft instead */
200                 if ( limit->lms_s_hard == 0
201                                 && limit->lms_s_soft > -1
202                                 && op->oq_search.rs_slimit > limit->lms_s_soft ) {
203                         op->oq_search.rs_slimit = limit->lms_s_soft;
204                         
205                 /* positive hard limit means abort */
206                 } else if ( limit->lms_s_hard > 0 ) {
207                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
208                         send_ldap_result( op, rs );
209                         rc = 0;
210                         goto finish;
211                 }
212                 
213                 /* negative hard limit means no limit */
214         }
215
216
217         dc.conn = op->o_conn;
218         dc.rs = rs;
219
220         /*
221          * Inits searches
222          */
223         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
224                 struct berval   realbase = op->o_req_dn;
225                 int             realscope = op->oq_search.rs_scope;
226                 ber_len_t       suffixlen = 0;
227                 struct berval   mbase = { 0, NULL }; 
228                 struct berval   mfilter = { 0, NULL };
229                 char            **mapped_attrs = NULL;
230
231                 if ( lsc->candidate != META_CANDIDATE ) {
232                         msgid[ i ] = -1;
233                         continue;
234                 }
235
236                 /* should we check return values? */
237                 if ( op->oq_search.rs_deref != -1 ) {
238                         ldap_set_option( lsc->ld, LDAP_OPT_DEREF,
239                                         ( void * )&op->oq_search.rs_deref);
240                 }
241                 if ( op->oq_search.rs_tlimit != -1 ) {
242                         ldap_set_option( lsc->ld, LDAP_OPT_TIMELIMIT,
243                                         ( void * )&op->oq_search.rs_tlimit);
244                 }
245                 if ( op->oq_search.rs_slimit != -1 ) {
246                         ldap_set_option( lsc->ld, LDAP_OPT_SIZELIMIT,
247                                         ( void * )&op->oq_search.rs_slimit);
248                 }
249
250                 dc.rwmap = &li->targets[ i ]->rwmap;
251
252                 /*
253                  * modifies the base according to the scope, if required
254                  */
255                 suffixlen = li->targets[ i ]->suffix.bv_len;
256                 if ( suffixlen > op->o_req_ndn.bv_len ) {
257                         switch ( op->oq_search.rs_scope ) {
258                         case LDAP_SCOPE_SUBTREE:
259                                 /*
260                                  * make the target suffix the new base
261                                  * FIXME: this is very forgiving, because
262                                  * illegal bases may be turned into 
263                                  * the suffix of the target.
264                                  */
265                                 if ( dnIsSuffix( &li->targets[ i ]->suffix,
266                                                 &op->o_req_ndn ) ) {
267                                         realbase = li->targets[ i ]->suffix;
268                                 } else {
269                                         /*
270                                          * this target is no longer candidate
271                                          */
272                                         msgid[ i ] = -1;
273                                         goto new_candidate;
274                                 }
275                                 break;
276
277                         case LDAP_SCOPE_ONELEVEL:
278                                 if ( is_one_level_rdn( li->targets[ i ]->suffix.bv_val,
279                                                 suffixlen - op->o_req_ndn.bv_len - 1 ) 
280                         && dnIsSuffix( &li->targets[ i ]->suffix, &op->o_req_ndn ) ) {
281                                         /*
282                                          * if there is exactly one level,
283                                          * make the target suffix the new
284                                          * base, and make scope "base"
285                                          */
286                                         realbase = li->targets[ i ]->suffix;
287                                         realscope = LDAP_SCOPE_BASE;
288                                         break;
289                                 } /* else continue with the next case */
290
291                         case LDAP_SCOPE_BASE:
292                                 /*
293                                  * this target is no longer candidate
294                                  */
295                                 msgid[ i ] = -1;
296                                 goto new_candidate;
297                         }
298
299                 }
300
301                 /*
302                  * Rewrite the search base, if required
303                  */
304                 dc.ctx = "searchBase";
305                 switch ( ldap_back_dn_massage( &dc, &realbase, &mbase ) ) {
306                 default:
307                         break;
308
309                 case REWRITE_REGEXEC_UNWILLING:
310                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
311                         rs->sr_text = "Operation not allowed";
312                         send_ldap_result( op, rs );
313                         rc = -1;
314                         goto finish;
315
316                 case REWRITE_REGEXEC_ERR:
317 #if 0
318                         rs->sr_err = LDAP_OTHER;
319                         rs->sr_text = "Rewrite error";
320                         send_ldap_result( op, rs );
321                         rc = -1;
322                         goto finish;
323 #endif 
324
325                         /*
326                          * this target is no longer candidate
327                          */
328                         msgid[ i ] = -1;
329                         goto new_candidate;
330                 }
331
332                 /*
333                  * Maps filter
334                  */
335                 rc = ldap_back_filter_map_rewrite( &dc,
336                                 op->oq_search.rs_filter,
337                                 &mfilter, BACKLDAP_MAP );
338                 if ( rc != 0 ) {
339                         /*
340                          * this target is no longer candidate
341                          */
342                         msgid[ i ] = -1;
343                         goto new_candidate;
344                 }
345
346                 /*
347                  * Maps required attributes
348                  */
349                 rc = ldap_back_map_attrs( &li->targets[ i ]->rwmap.rwm_at,
350                                 op->oq_search.rs_attrs, BACKLDAP_MAP,
351                                 &mapped_attrs );
352                 if ( rc != LDAP_SUCCESS ) {
353                         /*
354                          * this target is no longer candidate
355                          */
356                         msgid[ i ] = -1;
357                         goto new_candidate;
358                 }
359
360                 /*
361                  * Starts the search
362                  */
363                 msgid[ i ] = ldap_search( lsc->ld, mbase.bv_val, realscope,
364                                 mfilter.bv_val, mapped_attrs,
365                                 op->oq_search.rs_attrsonly ); 
366                 if ( mapped_attrs ) {
367                         free( mapped_attrs );
368                         mapped_attrs = NULL;
369                 }
370                 if ( mfilter.bv_val != op->oq_search.rs_filterstr.bv_val ) {
371                         free( mfilter.bv_val );
372                         mfilter.bv_val = NULL;
373                 }
374                 if ( mbase.bv_val != realbase.bv_val ) {
375                         free( mbase.bv_val );
376                         mbase.bv_val = NULL;
377                 }
378
379                 if ( msgid[ i ] == -1 ) {
380                         continue;
381                 }
382
383                 ++candidates;
384
385 new_candidate:;
386         }
387
388         initial_candidates = candidates;
389
390         /* We pull apart the ber result, stuff it into a slapd entry, and
391          * let send_search_entry stuff it back into ber format. Slow & ugly,
392          * but this is necessary for version matching, and for ACL processing.
393          */
394
395
396         /*
397          * In case there are no candidates, no cycle takes place...
398          *
399          * FIXME: we might use a queue, to balance the load 
400          * among the candidates
401          */
402         for ( rc = 0; candidates > 0; ) {
403                 int ab, gotit = 0;
404
405                 /* check for abandon */
406                 ab = op->o_abandon;
407
408                 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); lsc++, i++ ) {
409                         if ( msgid[ i ] == -1 ) {
410                                 continue;
411                         }
412                         
413                         if ( ab ) {
414                                 ldap_abandon( lsc->ld, msgid[ i ] );
415                                 rc = 0;
416                                 break;
417                         }
418
419                         if ( op->oq_search.rs_slimit > 0
420                                         && rs->sr_nentries == op->oq_search.rs_slimit ) {
421                                 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
422                                 rs->sr_v2ref = v2refs;
423                                 send_ldap_result( op, rs );
424                                 goto finish;
425                         }
426
427                         /*
428                          * FIXME: handle time limit as well?
429                          * Note that target servers are likely 
430                          * to handle it, so at some time we'll
431                          * get a LDAP_TIMELIMIT_EXCEEDED from
432                          * one of them ...
433                          */
434                         rc = ldap_result( lsc->ld, msgid[ i ],
435                                         0, &tv, &res );
436
437                         if ( rc == 0 ) {
438                                 continue;
439
440                         } else if ( rc == -1 ) {
441                                 /* something REALLY bad happened! */
442                                 ( void )meta_clear_unused_candidates( li,
443                                                 lc, -1, 0 );
444                                 rs->sr_err = LDAP_OTHER;
445                                 rs->sr_v2ref = v2refs;
446                                 send_ldap_result( op, rs );
447                                 
448                                 /* anything else needs be done? */
449                                 goto finish;
450
451                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
452                                 e = ldap_first_entry( lsc->ld, res );
453                                 meta_send_entry( op, rs, lc, i, e );
454
455                                 /*
456                                  * If scope is BASE, we need to jump out
457                                  * as soon as one entry is found; if
458                                  * the target pool is properly crafted,
459                                  * this should correspond to the sole
460                                  * entry that has the base DN
461                                  */
462                                 if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE
463                                                 && rs->sr_nentries > 0 ) {
464                                         candidates = 0;
465                                         sres = LDAP_SUCCESS;
466                                         break;
467                                 }
468                                 ldap_msgfree( res );
469                                 gotit = 1;
470
471                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
472                                 char            **references = NULL;
473                                 int             cnt;
474
475                                 /*
476                                  * FIXME: should we collect references
477                                  * and send them alltogether at the end?
478                                  */
479
480                                 rc = ldap_parse_reference( lsc->ld, res,
481                                                 &references, &rs->sr_ctrls, 1 );
482
483                                 if ( rc != LDAP_SUCCESS ) {
484                                         continue;
485                                 }
486
487                                 if ( references == NULL ) {
488                                         continue;
489                                 }
490
491                                 for ( cnt = 0; references[ cnt ]; cnt++ )
492                                         /* NO OP */ ;
493                                 
494                                 rs->sr_ref = ch_calloc( cnt + 1, sizeof( struct berval ) );
495
496                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
497                                         rs->sr_ref[ cnt ].bv_val = references[ cnt ];
498                                         rs->sr_ref[ cnt ].bv_len = strlen( references[ cnt ] );
499                                 }
500
501                                 /* ignore return value by now */
502                                 ( void )send_search_reference( op, rs );
503
504                                 /* cleanup */
505                                 if ( references ) {
506                                         ldap_value_free( references );
507                                         ch_free( rs->sr_ref );
508                                         rs->sr_ref = NULL;
509                                 }
510
511                                 if ( rs->sr_ctrls ) {
512                                         ldap_controls_free( rs->sr_ctrls );
513                                         rs->sr_ctrls = NULL;
514                                 }
515
516                         } else {
517                                 rs->sr_err = ldap_result2error( lsc->ld,
518                                                 res, 1 );
519                                 sres = ldap_back_map_result( rs );
520                                 if ( err != NULL ) {
521                                         free( err );
522                                 }
523                                 ldap_get_option( lsc->ld,
524                                                 LDAP_OPT_ERROR_STRING, &err );
525                                 if ( match.bv_val != NULL ) {
526                                         free( match.bv_val );
527                                 }
528                                 ldap_get_option( lsc->ld,
529                                                 LDAP_OPT_MATCHED_DN, &match.bv_val );
530
531 #ifdef NEW_LOGGING
532                                 LDAP_LOG( BACK_META, ERR,
533                                         "meta_back_search [%d] "
534                                         "match=\"%s\" err=\"%s\"\n",
535                                         i, match.bv_val, err );
536 #else /* !NEW_LOGGING */
537                                 Debug( LDAP_DEBUG_ANY,
538                                         "=>meta_back_search [%d] "
539                                         "match=\"%s\" err=\"%s\"\n",
540                                         i, match.bv_val, err ); 
541 #endif /* !NEW_LOGGING */
542                                 candidate_match++;
543                                 last = i;
544                                 rc = 0;
545
546                                 /*
547                                  * When no candidates are left,
548                                  * the outer cycle finishes
549                                  */
550                                 msgid[ i ] = -1;
551                                 --candidates;
552                         }
553                 }
554
555                 if ( ab ) {
556                         goto finish;
557                 }
558
559                 if ( gotit == 0 ) {
560                         tv.tv_sec = 0;
561                         tv.tv_usec = 100000;
562                         ldap_pvt_thread_yield();
563                 } else {
564                         tv.tv_sec = 0;
565                         tv.tv_usec = 0;
566                 }
567         }
568
569         if ( rc == -1 ) {
570                 /*
571                  * FIXME: need a strategy to handle errors
572                  */
573                 rc = meta_back_op_result( lc, op, rs );
574                 goto finish;
575         }
576
577         /*
578          * Rewrite the matched portion of the search base, if required
579          * 
580          * FIXME: only the last one gets caught!
581          */
582         if ( candidate_match == initial_candidates
583                         && match.bv_val != NULL && *match.bv_val ) {
584                 dc.ctx = "matchedDn";
585                 dc.rwmap = &li->targets[ last ]->rwmap;
586
587                 if ( ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
588                         mmatch.bv_val = NULL;
589                 }
590         }
591
592         /*
593          * In case we returned at least one entry, we return LDAP_SUCCESS
594          * otherwise, the latter error code we got
595          *
596          * FIXME: we should handle error codes and return the more 
597          * important/reasonable
598          */
599         if ( sres == LDAP_SUCCESS && v2refs ) {
600                 sres = LDAP_REFERRAL;
601         }
602         rs->sr_err = sres;
603         rs->sr_matched = mmatch.bv_val;
604         rs->sr_v2ref = v2refs;
605         send_ldap_result( op, rs );
606         rs->sr_matched = NULL;
607         rs->sr_v2ref = NULL;
608
609
610 finish:;
611         if ( match.bv_val ) {
612                 if ( mmatch.bv_val != match.bv_val ) {
613                         free( mmatch.bv_val );
614                 }
615                 free( match.bv_val );
616         }
617         
618         if ( err ) {
619                 free( err );
620         }
621         
622         if ( msgid ) {
623                 ch_free( msgid );
624         }
625
626         return rc;
627 }
628
629 static int
630 meta_send_entry(
631                 Operation       *op,
632                 SlapReply       *rs,
633                 struct metaconn *lc,
634                 int             target,
635                 LDAPMessage     *e
636 )
637 {
638         struct metainfo         *li = ( struct metainfo * )op->o_bd->be_private;
639         struct berval           a, mapped;
640         Entry                   ent;
641         BerElement              ber = *e->lm_ber;
642         Attribute               *attr, **attrp;
643         struct berval           dummy = { 0, NULL };
644         struct berval           *bv, bdn;
645         const char              *text;
646         dncookie                dc;
647
648         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
649                 return LDAP_DECODING_ERROR;
650         }
651
652         /*
653          * Rewrite the dn of the result, if needed
654          */
655         dc.rwmap = &li->targets[ target ]->rwmap;
656         dc.conn = op->o_conn;
657         dc.rs = rs;
658         dc.ctx = "searchResult";
659
660         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
661         if ( rs->sr_err != LDAP_SUCCESS) {
662                 return rs->sr_err;
663         }
664
665         /*
666          * Note: this may fail if the target host(s) schema differs
667          * from the one known to the meta, and a DN with unknown
668          * attributes is returned.
669          * 
670          * FIXME: should we log anything, or delegate to dnNormalize?
671          */
672         if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
673                 &op->o_tmpmemctx ) != LDAP_SUCCESS )
674         {
675                 return LDAP_INVALID_DN_SYNTAX;
676         }
677
678         /*
679          * cache dn
680          */
681         if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
682                 ( void )meta_dncache_update_entry( &li->cache,
683                                 &ent.e_nname, target );
684         }
685
686         ent.e_id = 0;
687         ent.e_attrs = 0;
688         ent.e_private = 0;
689         attrp = &ent.e_attrs;
690
691         dc.ctx = "searchAttrDN";
692         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
693                 int             last = 0;
694
695                 ldap_back_map( &li->targets[ target ]->rwmap.rwm_at, 
696                                 &a, &mapped, BACKLDAP_REMAP );
697                 if ( mapped.bv_val == NULL || mapped.bv_val[0] == '\0' ) {
698                         continue;
699                 }
700                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
701                 if ( attr == NULL ) {
702                         continue;
703                 }
704                 attr->a_flags = 0;
705                 attr->a_next = 0;
706                 attr->a_desc = NULL;
707                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
708                                 != LDAP_SUCCESS) {
709                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
710                                         != LDAP_SUCCESS) {
711 #ifdef NEW_LOGGING
712                                 LDAP_LOG( BACK_META, DETAIL1,
713                                         "slap_bv2undef_ad(%s): %s\n", mapped.bv_val, text, 0 );
714 #else /* !NEW_LOGGING */
715                                 Debug( LDAP_DEBUG_ANY,
716                                                 "slap_bv2undef_ad(%s): "
717                                                 "%s\n%s", mapped.bv_val, text, "" );
718 #endif /* !NEW_LOGGING */
719                                 ch_free( attr );
720                                 continue;
721                         }
722                 }
723
724                 /* no subschemaSubentry */
725                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
726
727                         /* 
728                          * We eat target's subschemaSubentry because
729                          * a search for this value is likely not
730                          * to resolve to the appropriate backend;
731                          * later, the local subschemaSubentry is
732                          * added.
733                          */
734                         ( void )ber_scanf( &ber, "x" /* [W] */ );
735
736                         ch_free(attr);
737                         continue;
738                 }
739
740                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
741                                 || attr->a_vals == NULL ) {
742                         attr->a_vals = &dummy;
743
744                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
745                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass ) {
746
747                         for ( last = 0; attr->a_vals[ last ].bv_val; ++last );
748
749                         for ( bv = attr->a_vals; bv->bv_val; bv++ ) {
750                                 ldap_back_map( &li->targets[ target ]->rwmap.rwm_oc,
751                                                 bv, &mapped, BACKLDAP_REMAP );
752                                 if ( mapped.bv_val == NULL || mapped.bv_val[0] == '\0') {
753                                         free( bv->bv_val );
754                                         bv->bv_val = NULL;
755                                         if ( --last < 0 ) {
756                                                 break;
757                                         }
758                                         *bv = attr->a_vals[ last ];
759                                         attr->a_vals[ last ].bv_val = NULL;
760                                         bv--;
761
762                                 } else if ( mapped.bv_val != bv->bv_val ) {
763                                         free( bv->bv_val );
764                                         ber_dupbv( bv, &mapped );
765                                 }
766                         }
767                 /*
768                  * It is necessary to try to rewrite attributes with
769                  * dn syntax because they might be used in ACLs as
770                  * members of groups; since ACLs are applied to the
771                  * rewritten stuff, no dn-based subecj clause could
772                  * be used at the ldap backend side (see
773                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
774                  * The problem can be overcome by moving the dn-based
775                  * ACLs to the target directory server, and letting
776                  * everything pass thru the ldap backend.
777                  */
778                 } else if ( attr->a_desc->ad_type->sat_syntax ==
779                                 slap_schema.si_syn_distinguishedName ) {
780                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
781                 }
782
783                 if ( last && attr->a_desc->ad_type->sat_equality &&
784                         attr->a_desc->ad_type->sat_equality->smr_normalize ) {
785                         int i;
786
787                         attr->a_nvals = ch_malloc((last + 1)*sizeof(struct berval));
788                         for ( i = 0; i<last; i++ ) {
789                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
790                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
791                                         attr->a_desc->ad_type->sat_syntax,
792                                         attr->a_desc->ad_type->sat_equality,
793                                         &attr->a_vals[i], &attr->a_nvals[i],
794                                         op->o_tmpmemctx );
795                         }
796                         attr->a_nvals[i].bv_val = NULL;
797                         attr->a_nvals[i].bv_len = 0;
798                 } else {
799                         attr->a_nvals = attr->a_vals;
800                 }
801
802                 *attrp = attr;
803                 attrp = &attr->a_next;
804         }
805         rs->sr_entry = &ent;
806         rs->sr_attrs = op->oq_search.rs_attrs;
807         send_search_entry( op, rs );
808         rs->sr_entry = NULL;
809         rs->sr_attrs = NULL;
810         while ( ent.e_attrs ) {
811                 attr = ent.e_attrs;
812                 ent.e_attrs = attr->a_next;
813                 if ( attr->a_vals != &dummy ) {
814                         ber_bvarray_free( attr->a_vals );
815                 }
816                 free( attr );
817         }
818         
819         if ( ent.e_dn && ent.e_dn != bdn.bv_val ) {
820                 free( ent.e_dn );
821         }
822         if ( ent.e_ndn ) {
823                 free( ent.e_ndn );
824         }
825
826         return LDAP_SUCCESS;
827 }
828
829 static int
830 is_one_level_rdn(
831                 const char      *rdn,
832                 int             from
833 )
834 {
835         for ( ; from--; ) {
836                 if ( DN_SEPARATOR( rdn[ from ] ) ) {
837                         return 0;
838                 }
839         }
840
841         return 1;
842 }
843