001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.server.core.jndi;
021
022
023import java.io.Serializable;
024import java.text.ParseException;
025import java.util.ArrayList;
026import java.util.Hashtable;
027import java.util.List;
028
029import javax.naming.Name;
030import javax.naming.NamingEnumeration;
031import javax.naming.NamingException;
032import javax.naming.Reference;
033import javax.naming.Referenceable;
034import javax.naming.directory.Attributes;
035import javax.naming.directory.DirContext;
036import javax.naming.directory.InvalidAttributeIdentifierException;
037import javax.naming.directory.InvalidAttributeValueException;
038import javax.naming.directory.InvalidAttributesException;
039import javax.naming.directory.InvalidSearchFilterException;
040import javax.naming.directory.ModificationItem;
041import javax.naming.directory.SearchControls;
042import javax.naming.directory.SearchResult;
043import javax.naming.event.EventDirContext;
044import javax.naming.event.NamingListener;
045import javax.naming.ldap.LdapName;
046import javax.naming.spi.DirStateFactory;
047import javax.naming.spi.DirectoryManager;
048
049import org.apache.directory.api.ldap.model.entry.Attribute;
050import org.apache.directory.api.ldap.model.entry.AttributeUtils;
051import org.apache.directory.api.ldap.model.entry.Entry;
052import org.apache.directory.api.ldap.model.entry.Modification;
053import org.apache.directory.api.ldap.model.exception.LdapException;
054import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeTypeException;
055import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException;
056import org.apache.directory.api.ldap.model.filter.AndNode;
057import org.apache.directory.api.ldap.model.filter.BranchNode;
058import org.apache.directory.api.ldap.model.filter.EqualityNode;
059import org.apache.directory.api.ldap.model.filter.ExprNode;
060import org.apache.directory.api.ldap.model.filter.FilterParser;
061import org.apache.directory.api.ldap.model.filter.PresenceNode;
062import org.apache.directory.api.ldap.model.filter.SimpleNode;
063import org.apache.directory.api.ldap.model.message.AliasDerefMode;
064import org.apache.directory.api.ldap.model.message.SearchScope;
065import org.apache.directory.api.ldap.model.name.Dn;
066import org.apache.directory.api.ldap.util.JndiUtils;
067import org.apache.directory.api.util.Strings;
068import org.apache.directory.server.core.api.CoreSession;
069import org.apache.directory.server.core.api.DirectoryService;
070import org.apache.directory.server.core.api.LdapPrincipal;
071import org.apache.directory.server.core.api.entry.ServerEntryUtils;
072import org.apache.directory.server.core.api.event.DirectoryListener;
073import org.apache.directory.server.core.api.event.NotificationCriteria;
074import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
075import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
076import org.apache.directory.server.i18n.I18n;
077
078
079/**
080 * The DirContext implementation for the Server Side JNDI LDAP provider.
081 *
082 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
083 */
084public abstract class ServerDirContext extends ServerContext implements EventDirContext
085{
086    // ------------------------------------------------------------------------
087    // Constructors
088    // ------------------------------------------------------------------------
089
090    /**
091     * Creates a new ServerDirContext by reading the PROVIDER_URL to resolve the
092     * distinguished name for this context.
093     *
094     * @param service the parent service that manages this context
095     * @param env the environment used for this context
096     * @throws NamingException if something goes wrong
097     */
098    public ServerDirContext( DirectoryService service, Hashtable<String, Object> env ) throws Exception
099    {
100        super( service, env );
101    }
102
103
104    /**
105     * Creates a new ServerDirContext with a distinguished name which is used to
106     * set the PROVIDER_URL to the distinguished name for this context.
107     *
108     * @param service The DirectoryService instance
109     * @param principal the principal which is propagated
110     * @param dn the distinguished name of this context
111     * @throws Exception If we weren't able to create the ServerDirContext instance
112     */
113    public ServerDirContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws Exception
114    {
115        super( service, principal, dn );
116    }
117
118
119    // ------------------------------------------------------------------------
120    // DirContext Implementations
121    // ------------------------------------------------------------------------
122
123    public ServerDirContext( DirectoryService service, CoreSession session, Name bindDn ) throws Exception
124    {
125        super( service, session, bindDn );
126    }
127
128
129    /**
130     * @see javax.naming.directory.DirContext#getAttributes(java.lang.String)
131     */
132    public Attributes getAttributes( String name ) throws NamingException
133    {
134        return getAttributes( new LdapName( name ) );
135    }
136
137
138    /**
139     * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name)
140     */
141    public Attributes getAttributes( Name name ) throws NamingException
142    {
143        Attributes attrs = null;
144
145        try
146        {
147            attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( JndiUtils.fromName( name ) ) ) );
148        }
149        catch ( Exception e )
150        {
151            JndiUtils.wrap( e );
152        }
153
154        return attrs;
155    }
156
157
158    /**
159     * @see javax.naming.directory.DirContext#getAttributes(java.lang.String,
160     *      java.lang.String[])
161     */
162    public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
163    {
164        return getAttributes( new LdapName( name ), attrIds );
165    }
166
167
168    /**
169     * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name,
170     *      java.lang.String[])
171     */
172    public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
173    {
174        Attributes attrs = null;
175        try
176        {
177            attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( JndiUtils.fromName( name ) ),
178                attrIds ) );
179        }
180        catch ( Exception e )
181        {
182            JndiUtils.wrap( e );
183        }
184
185        return attrs;
186    }
187
188
189    /**
190     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
191     *      int, javax.naming.directory.Attributes)
192     */
193    public void modifyAttributes( String name, int modOp, Attributes attrs ) throws NamingException
194    {
195        modifyAttributes( new LdapName( name ), modOp, AttributeUtils.toCaseInsensitive( attrs ) );
196    }
197
198
199    /**
200     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
201     *      int, javax.naming.directory.Attributes)
202     */
203    public void modifyAttributes( Name name, int modOp, Attributes attrs ) throws NamingException
204    {
205        List<ModificationItem> modItems = null;
206
207        if ( attrs != null )
208        {
209            modItems = new ArrayList<>( attrs.size() );
210            NamingEnumeration<? extends javax.naming.directory.Attribute> e = attrs.getAll();
211
212            while ( e.hasMore() )
213            {
214                modItems.add( new ModificationItem( modOp, e.next() ) );
215            }
216        }
217
218        List<Modification> newMods = null;
219
220        try
221        {
222            newMods = ServerEntryUtils.convertToServerModification(
223                modItems,
224                getDirectoryService().getSchemaManager() );
225        }
226        catch ( LdapNoSuchAttributeException lnsae )
227        {
228            throw new InvalidAttributesException( lnsae.getMessage() );
229        }
230        catch ( LdapException le )
231        {
232            throw new InvalidAttributeValueException( le.getMessage() );
233        }
234
235        try
236        {
237            doModifyOperation( buildTarget( JndiUtils.fromName( name ) ), newMods );
238        }
239        catch ( Exception e )
240        {
241            JndiUtils.wrap( e );
242        }
243    }
244
245
246    /**
247     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
248     *      javax.naming.directory.ModificationItem[])
249     */
250    public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
251    {
252        modifyAttributes( new LdapName( name ), mods );
253    }
254
255
256    /**
257     * @see javax.naming.directory.DirContext#modifyAttributes(
258     * javax.naming.Name, javax.naming.directory.ModificationItem[])
259     */
260    public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
261    {
262        List<Modification> newMods;
263
264        try
265        {
266            newMods = ServerEntryUtils
267                .toServerModification( mods, getDirectoryService().getSchemaManager() );
268        }
269        catch ( LdapException le )
270        {
271            throw new InvalidAttributesException( le.getMessage() );
272        }
273
274        try
275        {
276            doModifyOperation( buildTarget( JndiUtils.fromName( name ) ), newMods );
277        }
278        catch ( Exception e )
279        {
280            JndiUtils.wrap( e );
281        }
282    }
283
284
285    /**
286     * @see javax.naming.directory.DirContext#modifyAttributes(
287     * javax.naming.Name, javax.naming.directory.ModificationItem[])
288     * 
289     * @param name The entry to modify
290     * @param mods The modifications to apply
291     * @throws NamingException If the modify operation failed
292     */
293    public void modifyAttributes( Name name, List<ModificationItem> mods ) throws NamingException
294    {
295        List<Modification> newMods;
296        try
297        {
298            newMods = ServerEntryUtils
299                .convertToServerModification( mods,
300                    getDirectoryService().getSchemaManager() );
301        }
302        catch ( LdapException le )
303        {
304            throw new InvalidAttributesException( le.getMessage() );
305        }
306
307        try
308        {
309            doModifyOperation( buildTarget( JndiUtils.fromName( name ) ), newMods );
310        }
311        catch ( Exception e )
312        {
313            JndiUtils.wrap( e );
314        }
315    }
316
317
318    /**
319     * @see javax.naming.directory.DirContext#bind(java.lang.String,
320     *      java.lang.Object, javax.naming.directory.Attributes)
321     */
322    public void bind( String name, Object obj, Attributes attrs ) throws NamingException
323    {
324        bind( new LdapName( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
325    }
326
327
328    /**
329     * @see javax.naming.directory.DirContext#bind(javax.naming.Name,
330     *      java.lang.Object, javax.naming.directory.Attributes)
331     */
332    public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
333    {
334        if ( ( null == obj ) && ( null == attrs ) )
335        {
336            throw new NamingException( I18n.err( I18n.ERR_499 ) );
337        }
338
339        // A null attrs defaults this to the Context.bind() operation
340        if ( null == attrs )
341        {
342            super.bind( name, obj );
343            return;
344        }
345
346        Dn target = buildTarget( JndiUtils.fromName( name ) );
347
348        Entry serverEntry = null;
349
350        try
351        {
352            serverEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( attrs ), target,
353                getDirectoryService().getSchemaManager() );
354        }
355        catch ( LdapInvalidAttributeTypeException liate )
356        {
357            throw new InvalidAttributesException( liate.getMessage() );
358        }
359
360        // No object binding so we just add the attributes
361        if ( null == obj )
362        {
363            Entry clone = serverEntry.clone();
364            try
365            {
366                doAddOperation( target, clone );
367            }
368            catch ( Exception e )
369            {
370                JndiUtils.wrap( e );
371            }
372            return;
373        }
374
375        // First, use state factories to do a transformation
376        DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, getEnvironment(), attrs );
377        Entry outServerEntry = null;
378
379        try
380        {
381            outServerEntry = ServerEntryUtils.toServerEntry(
382                res.getAttributes(), target, getDirectoryService().getSchemaManager() );
383        }
384        catch ( LdapInvalidAttributeTypeException le )
385        {
386            throw new InvalidAttributesException( le.getMessage() );
387        }
388
389        if ( outServerEntry != serverEntry )
390        {
391            Entry clone = serverEntry.clone();
392
393            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
394            {
395                for ( Attribute attribute : outServerEntry )
396                {
397                    try
398                    {
399                        clone.put( attribute );
400                    }
401                    catch ( LdapException e )
402                    {
403                        // TODO Auto-generated catch block
404                    }
405                }
406            }
407
408            try
409            {
410                // setup the op context
411                doAddOperation( target, clone );
412            }
413            catch ( Exception e )
414            {
415                JndiUtils.wrap( e );
416            }
417
418            return;
419        }
420
421        // Check for Referenceable
422        if ( obj instanceof Referenceable )
423        {
424            throw new NamingException( I18n.err( I18n.ERR_493 ) );
425        }
426
427        // Store different formats
428        if ( obj instanceof Reference )
429        {
430            // Store as ref and add outAttrs
431            throw new NamingException( I18n.err( I18n.ERR_494 ) );
432        }
433        else if ( obj instanceof Serializable )
434        {
435            // Serialize and add outAttrs
436            Entry clone = serverEntry.clone();
437
438            if ( outServerEntry.size() > 0 )
439            {
440                for ( Attribute attribute : outServerEntry )
441                {
442                    try
443                    {
444                        clone.put( attribute );
445                    }
446                    catch ( LdapException le )
447                    {
448                        throw new InvalidAttributesException( le.getMessage() );
449                    }
450                }
451            }
452
453            try
454            {
455                // Serialize object into entry attributes and add it.
456                JavaLdapSupport.serialize( serverEntry, obj, getDirectoryService().getSchemaManager() );
457
458                // setup the op context
459                doAddOperation( target, clone );
460            }
461            catch ( Exception e )
462            {
463                JndiUtils.wrap( e );
464            }
465        }
466        else if ( obj instanceof DirContext )
467        {
468            // Grab attributes and merge with outAttrs
469            Entry entry = null;
470
471            try
472            {
473                entry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ), target,
474                    getDirectoryService().getSchemaManager() );
475            }
476            catch ( LdapInvalidAttributeTypeException liate )
477            {
478                throw new InvalidAttributeIdentifierException( liate.getMessage() );
479            }
480
481            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
482            {
483                for ( Attribute attribute : outServerEntry )
484                {
485                    try
486                    {
487                        entry.put( attribute );
488                    }
489                    catch ( LdapException le )
490                    {
491                        throw new InvalidAttributeValueException( le.getMessage() );
492                    }
493                }
494            }
495
496            try
497            {
498                // setup the op context
499                doAddOperation( target, entry );
500            }
501            catch ( Exception e )
502            {
503                JndiUtils.wrap( e );
504            }
505        }
506        else
507        {
508            throw new NamingException( I18n.err( I18n.ERR_495, obj ) );
509        }
510    }
511
512
513    /**
514     * @see javax.naming.directory.DirContext#rebind(java.lang.String,
515     *      java.lang.Object, javax.naming.directory.Attributes)
516     */
517    public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
518    {
519        rebind( new LdapName( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
520    }
521
522
523    /**
524     * @see javax.naming.directory.DirContext#rebind(javax.naming.Name,
525     *      java.lang.Object, javax.naming.directory.Attributes)
526     */
527    public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
528    {
529        Dn target = buildTarget( JndiUtils.fromName( name ) );
530
531        try
532        {
533            HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( getSession(), target );
534
535            if ( getDirectoryService().getOperationManager().hasEntry( hasEntryContext ) )
536            {
537                doDeleteOperation( target );
538            }
539        }
540        catch ( Exception e )
541        {
542            JndiUtils.wrap( e );
543        }
544
545        bind( name, obj, AttributeUtils.toCaseInsensitive( attrs ) );
546    }
547
548
549    /**
550     * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String,
551     *      javax.naming.directory.Attributes)
552     */
553    public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
554    {
555        Attributes attributes = AttributeUtils.toCaseInsensitive( attrs );
556        return createSubcontext( new LdapName( name ), attributes );
557    }
558
559
560    /**
561     * @see javax.naming.directory.DirContext#createSubcontext(
562     * javax.naming.Name, javax.naming.directory.Attributes)
563     */
564    public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
565    {
566        if ( null == attrs )
567        {
568            return ( DirContext ) super.createSubcontext( name );
569        }
570
571        Dn target = buildTarget( JndiUtils.fromName( name ) );
572
573        attrs = AttributeUtils.toCaseInsensitive( attrs );
574        Attributes attributes = ( Attributes ) attrs.clone();
575
576        // Add the new context to the server which as a side effect adds
577        try
578        {
579            Entry serverEntry = ServerEntryUtils.toServerEntry( attributes,
580                target, getDirectoryService().getSchemaManager() );
581            doAddOperation( target, serverEntry );
582        }
583        catch ( Exception e )
584        {
585            JndiUtils.wrap( e );
586        }
587
588        // Initialize the new context
589        ServerLdapContext ctx = null;
590
591        try
592        {
593            ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), JndiUtils.toName( target ) );
594        }
595        catch ( Exception e )
596        {
597            JndiUtils.wrap( e );
598        }
599
600        return ctx;
601    }
602
603
604    /**
605     * Presently unsupported operation!
606     */
607    public DirContext getSchema( Name name ) throws NamingException
608    {
609        throw new UnsupportedOperationException();
610    }
611
612
613    /**
614     * Presently unsupported operation!
615     */
616    public DirContext getSchema( String name ) throws NamingException
617    {
618        throw new UnsupportedOperationException();
619    }
620
621
622    /**
623     * Presently unsupported operation!
624     */
625    public DirContext getSchemaClassDefinition( Name name ) throws NamingException
626    {
627        throw new UnsupportedOperationException();
628    }
629
630
631    /**
632     * Presently unsupported operation!
633     */
634    public DirContext getSchemaClassDefinition( String name ) throws NamingException
635    {
636        throw new UnsupportedOperationException();
637    }
638
639
640    // ------------------------------------------------------------------------
641    // Search Operation Implementations
642    // ------------------------------------------------------------------------
643
644    /**
645     * @see javax.naming.directory.DirContext#search(java.lang.String,
646     *      javax.naming.directory.Attributes)
647     */
648    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes ) throws NamingException
649    {
650        return search( new LdapName( name ), matchingAttributes, null );
651    }
652
653
654    /**
655     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
656     *      javax.naming.directory.Attributes)
657     */
658    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes ) throws NamingException
659    {
660        return search( name, AttributeUtils.toCaseInsensitive( matchingAttributes ), null );
661    }
662
663
664    /**
665     * @see javax.naming.directory.DirContext#search(java.lang.String,
666     *      javax.naming.directory.Attributes, java.lang.String[])
667     */
668    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes,
669        String[] attributesToReturn ) throws NamingException
670    {
671        return search( new LdapName( name ), AttributeUtils.toCaseInsensitive( matchingAttributes ), attributesToReturn );
672    }
673
674
675    /**
676     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
677     *      javax.naming.directory.Attributes, java.lang.String[])
678     */
679    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes, String[] attributesToReturn )
680        throws NamingException
681    {
682        SearchControls ctls = new SearchControls();
683        Dn target = buildTarget( JndiUtils.fromName( name ) );
684
685        // If we need to return specific attributes add em to the SearchControls
686        if ( null != attributesToReturn )
687        {
688            ctls.setReturningAttributes( attributesToReturn );
689        }
690
691        // If matchingAttributes is null/empty use a match for everything filter
692        matchingAttributes = AttributeUtils.toCaseInsensitive( matchingAttributes );
693
694        if ( ( null == matchingAttributes ) || ( matchingAttributes.size() <= 0 ) )
695        {
696            PresenceNode filter = new PresenceNode( objectClassAT );
697            AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
698            try
699            {
700                EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filter, ctls );
701                return new NamingEnumerationAdapter( cursor );
702            }
703            catch ( Exception e )
704            {
705                JndiUtils.wrap( e );
706            }
707        }
708
709        // Handle simple filter expressions without multiple terms
710        if ( matchingAttributes.size() == 1 )
711        {
712            NamingEnumeration<? extends javax.naming.directory.Attribute> list = matchingAttributes.getAll();
713            javax.naming.directory.Attribute attr = list.next();
714            list.close();
715
716            if ( attr.size() == 1 )
717            {
718                Object value = attr.get();
719                SimpleNode<?> node;
720                String attributeType = attr.getID();
721
722                if ( value instanceof byte[] )
723                {
724                    node = new EqualityNode<>( attributeType, ( byte[] ) value  );
725                }
726                else
727                {
728                    node = new EqualityNode<>( attributeType,  ( String ) value );
729                }
730
731                AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
732
733                try
734                {
735                    EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, node, ctls );
736                    return new NamingEnumerationAdapter( cursor );
737                }
738                catch ( Exception e )
739                {
740                    JndiUtils.wrap( e );
741                    return null; // shut compiler up
742                }
743            }
744        }
745
746        /*
747         * Go through the set of attributes using each attribute value pair as
748         * an attribute value assertion within one big AND filter expression.
749         */
750        javax.naming.directory.Attribute attr;
751        SimpleNode node;
752        BranchNode filter = new AndNode();
753        NamingEnumeration<? extends javax.naming.directory.Attribute> list = matchingAttributes.getAll();
754
755        // Loop through each attribute value pair
756        while ( list.hasMore() )
757        {
758            attr = list.next();
759
760            /*
761             * According to JNDI if an attribute in the matchingAttributes
762             * list does not have any values then we match for just the presence
763             * of the attribute in the entry
764             */
765            if ( attr.size() == 0 )
766            {
767                filter.addNode( new PresenceNode( attr.getID() ) );
768                continue;
769            }
770
771            /*
772             * With 1 or more value we build a set of simple nodes and add them
773             * to the AND node - each attribute value pair is a simple Ava node.
774             */
775            for ( int ii = 0; ii < attr.size(); ii++ )
776            {
777                Object val = attr.get( ii );
778
779                // Add simpel Ava node if its value is a String
780                if ( val instanceof String )
781                {
782                    node = new EqualityNode<String>( attr.getID(), ( String ) val );
783                    filter.addNode( node );
784                }
785            }
786        }
787
788        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
789        try
790        {
791            EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filter, ctls );
792            return new NamingEnumerationAdapter( cursor );
793        }
794        catch ( Exception e )
795        {
796            JndiUtils.wrap( e );
797            return null; // shut compiler up
798        }
799    }
800
801
802    /**
803     * @see javax.naming.directory.DirContext#search(java.lang.String,
804     *      java.lang.String, javax.naming.directory.SearchControls)
805     */
806    public NamingEnumeration<SearchResult> search( String name, String filter, SearchControls cons )
807        throws NamingException
808    {
809        return search( new LdapName( name ), filter, cons );
810    }
811
812
813    /**
814     * A search overload that is used for optimizing search handling in the
815     * LDAP protocol provider which deals with an ExprNode instance rather than
816     * a String for the filter.
817     *
818     * @param name the relative name of the object serving as the search base
819     * @param filter the search filter as an expression tree
820     * @param cons the search controls to use
821     * @return an enumeration over the SearchResults
822     * @throws NamingException if there are problems performing the search
823     */
824    public NamingEnumeration<SearchResult> search( Name name, ExprNode filter, SearchControls cons )
825        throws NamingException
826    {
827        Dn target = buildTarget( JndiUtils.fromName( name ) );
828        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
829        try
830        {
831            return new NamingEnumerationAdapter( doSearchOperation( target, aliasDerefMode, filter, cons ) );
832        }
833        catch ( Exception e )
834        {
835            JndiUtils.wrap( e );
836            return null; // shut compiler up
837        }
838    }
839
840
841    /**
842     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
843     *      java.lang.String, javax.naming.directory.SearchControls)
844     */
845    public NamingEnumeration<SearchResult> search( Name name, String filter, SearchControls cons )
846        throws NamingException
847    {
848        ExprNode filterNode;
849        Dn target = buildTarget( JndiUtils.fromName( name ) );
850
851        try
852        {
853            filterNode = FilterParser.parse( schemaManager, filter );
854        }
855        catch ( ParseException pe )
856        {
857            InvalidSearchFilterException isfe = new InvalidSearchFilterException( I18n.err( I18n.ERR_500, filter ) );
858            isfe.setRootCause( pe );
859            throw isfe;
860        }
861        
862        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
863
864        try
865        {
866            EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filterNode, cons );
867            return new NamingEnumerationAdapter( cursor );
868        }
869        catch ( Exception e )
870        {
871            JndiUtils.wrap( e );
872            return null; // shut compiler up
873        }
874    }
875
876
877    /**
878     * @see javax.naming.directory.DirContext#search(java.lang.String,
879     *      java.lang.String, java.lang.Object[],
880     *      javax.naming.directory.SearchControls)
881     */
882    public NamingEnumeration<SearchResult> search( String name, String filterExpr, Object[] filterArgs,
883        SearchControls cons ) throws NamingException
884    {
885        return search( new LdapName( name ), filterExpr, filterArgs, cons );
886    }
887
888
889    /**
890     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
891     *      java.lang.String, java.lang.Object[],
892     *      javax.naming.directory.SearchControls)
893     */
894    public NamingEnumeration<SearchResult> search( Name name, String filterExpr, Object[] filterArgs,
895        SearchControls cons ) throws NamingException
896    {
897        int start;
898        int index;
899
900        StringBuilder buf = new StringBuilder( filterExpr );
901
902        // Scan until we hit the end of the string buffer
903        for ( int ii = 0; ii < buf.length(); ii++ )
904        {
905            try
906            {
907                // Advance until we hit the start of a variable
908                while ( ii < buf.length() && '{' != buf.charAt( ii ) )
909                {
910                    ii++;
911                }
912
913                // Record start of variable at '{'
914                start = ii;
915
916                // Advance to the end of a variable at '}'
917                while ( '}' != buf.charAt( ii ) )
918                {
919                    ii++;
920                }
921            }
922            catch ( IndexOutOfBoundsException e )
923            {
924                // End of filter so done.
925                break;
926            }
927
928            // Parse index
929            index = Integer.parseInt( buf.substring( start + 1, ii ) );
930
931            if ( filterArgs[index] instanceof String )
932            {
933                /*
934                 * Replace the '{ i }' with the string representation of the value
935                 * held in the filterArgs array at index index.
936                 */
937                buf.replace( start, ii + 1, ( String ) filterArgs[index] );
938            }
939            else if ( filterArgs[index] instanceof byte[] )
940            {
941                String hexstr = "#" + Strings.toHexString( ( byte[] ) filterArgs[index] );
942                buf.replace( start, ii + 1, hexstr );
943            }
944            else
945            {
946                /*
947                 * Replace the '{ i }' with the string representation of the value
948                 * held in the filterArgs array at index index.
949                 */
950                buf.replace( start, ii + 1, filterArgs[index].toString() );
951            }
952        }
953
954        return search( name, buf.toString(), cons );
955    }
956
957
958    // ------------------------------------------------------------------------
959    // EventDirContext implementations
960    // ------------------------------------------------------------------------
961
962    public void addNamingListener( Name name, String filterStr, SearchControls searchControls,
963        NamingListener namingListener ) throws NamingException
964    {
965        ExprNode filter;
966
967        try
968        {
969            filter = FilterParser.parse( schemaManager, filterStr );
970        }
971        catch ( Exception e )
972        {
973            NamingException e2 = new NamingException( I18n.err( I18n.ERR_501, filterStr ) );
974            e2.setRootCause( e );
975            throw e2;
976        }
977
978        try
979        {
980            DirectoryListener listener = new EventListenerAdapter( ( ServerLdapContext ) this, namingListener );
981            NotificationCriteria criteria = new NotificationCriteria( schemaManager );
982            criteria.setFilter( filter );
983            criteria.setScope( SearchScope.getSearchScope( searchControls.getSearchScope() ) );
984            criteria.setAliasDerefMode( AliasDerefMode.getEnum( getEnvironment() ) );
985            criteria.setBase( buildTarget( JndiUtils.fromName( name ) ) );
986
987            getDirectoryService().getEventService().addListener( listener, criteria );
988            getListeners().put( namingListener, listener );
989        }
990        catch ( Exception e )
991        {
992            JndiUtils.wrap( e );
993        }
994    }
995
996
997    public void addNamingListener( String name, String filter, SearchControls searchControls,
998        NamingListener namingListener ) throws NamingException
999    {
1000        addNamingListener( new LdapName( name ), filter, searchControls, namingListener );
1001    }
1002
1003
1004    public void addNamingListener( Name name, String filterExpr, Object[] filterArgs, SearchControls searchControls,
1005        NamingListener namingListener ) throws NamingException
1006    {
1007        int start;
1008        StringBuilder buf = new StringBuilder( filterExpr );
1009
1010        // Scan until we hit the end of the string buffer
1011        for ( int ii = 0; ii < buf.length(); ii++ )
1012        {
1013            // Advance until we hit the start of a variable
1014            while ( '{' != buf.charAt( ii ) )
1015            {
1016                ii++;
1017            }
1018
1019            // Record start of variable at '{'
1020            start = ii;
1021
1022            // Advance to the end of a variable at '}'
1023            while ( '}' != buf.charAt( ii ) )
1024            {
1025                ii++;
1026            }
1027
1028            /*
1029             * Replace the '{ i }' with the string representation of the value
1030             * held in the filterArgs array at index index.
1031             */
1032            buf.replace( start, ii + 1, filterArgs[ii].toString() );
1033        }
1034
1035        addNamingListener( name, buf.toString(), searchControls, namingListener );
1036    }
1037
1038
1039    public void addNamingListener( String name, String filter, Object[] objects, SearchControls searchControls,
1040        NamingListener namingListener ) throws NamingException
1041    {
1042        addNamingListener( new LdapName( name ), filter, objects, searchControls, namingListener );
1043    }
1044}