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