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