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 *    https://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.api.ldap.codec.api;
021
022
023import org.apache.directory.api.asn1.DecoderException;
024import org.apache.directory.api.asn1.ber.AbstractContainer;
025import org.apache.directory.api.asn1.ber.tlv.TLV;
026import org.apache.directory.api.ldap.codec.LdapMessageGrammar;
027import org.apache.directory.api.ldap.codec.LdapStatesEnum;
028import org.apache.directory.api.ldap.codec.search.ConnectorFilter;
029import org.apache.directory.api.ldap.codec.search.Filter;
030import org.apache.directory.api.ldap.codec.search.PresentFilter;
031import org.apache.directory.api.ldap.model.entry.Attribute;
032import org.apache.directory.api.ldap.model.entry.Modification;
033import org.apache.directory.api.ldap.model.message.Control;
034import org.apache.directory.api.ldap.model.message.ExtendedResponse;
035import org.apache.directory.api.ldap.model.message.LdapResult;
036import org.apache.directory.api.ldap.model.message.Message;
037import org.apache.directory.api.ldap.model.message.ResultResponse;
038
039
040/**
041 * The LdapMessage container stores all the messages decoded by the Asn1Decoder.
042 * When dealing with an encoding PDU, we will obtain a LdapMessage in the
043 * container.
044 *
045 * @param <E> The decorated message
046 * 
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049public class LdapMessageContainer<E extends Message> extends AbstractContainer
050{
051    /** The Message being decoded */
052    private E message;
053
054    /** checks if attribute is binary */
055    private BinaryAttributeDetector binaryAttributeDetector;
056
057    /** The message ID */
058    private int messageId;
059
060    /** The current control */
061    private Control currentControl;
062    
063    /** The current control factory, if any */
064    private ControlFactory<?> controlFactory;
065    
066    /** The current Intermediate response factory */
067    private IntermediateOperationFactory intermediateFactory;
068    
069    /** The current Extended operation factory */
070    private ExtendedOperationFactory extendedFactory;
071
072    /** The codec service */
073    private final LdapApiService codec;
074    
075    /** The current LdapResult for a response */
076    private LdapResult ldapResult;
077    
078    /** The current attribute being decoded */
079    private Attribute currentAttribute;
080
081    /** A local storage for the MODIFY operation */
082    private Modification currentModification;
083    
084    /** The SearchRequest TLV id */
085    private int tlvId;
086
087    /** A temporary storage for a terminal Filter */
088    private Filter terminalFilter;
089
090    /** The current filter. This is used while decoding a PDU */
091    private Filter currentFilter;
092
093    /** The global filter. This is used while decoding a PDU */
094    private Filter topFilter;
095
096
097    /**
098     * Creates a new LdapMessageContainer object. We will store ten grammars,
099     * it's enough ...
100     * 
101     * @param codec The LDAP service instance
102     */
103    public LdapMessageContainer( LdapApiService codec )
104    {
105        this( codec, new DefaultConfigurableBinaryAttributeDetector() );
106    }
107
108
109    /**
110     * Creates a new LdapMessageContainer object. 
111     *
112     * @param codec The LDAP service instance
113     * @param binaryAttributeDetector checks if an attribute is binary
114     */
115    public LdapMessageContainer( LdapApiService codec, BinaryAttributeDetector binaryAttributeDetector )
116    {
117        super();
118        this.codec = codec;
119        setGrammar( LdapMessageGrammar.getInstance() );
120        this.binaryAttributeDetector = binaryAttributeDetector;
121        setTransition( LdapStatesEnum.START_STATE );
122    }
123
124
125    /**
126     * Gets the {@link LdapApiService} associated with this Container.
127     *
128     * @return The LDAP service instance
129     */
130    public LdapApiService getLdapCodecService()
131    {
132        return codec;
133    }
134
135
136    /**
137     * @return Returns the ldapMessage.
138     */
139    public E getMessage()
140    {
141        return message;
142    }
143
144
145    /**
146     * Set a Message Object into the container. It will be completed by the
147     * ldapDecoder.
148     *
149     * @param message The message to set.
150     */
151    public void setMessage( E message )
152    {
153        this.message = message;
154    }
155
156
157    /**
158     * {@inheritDoc}
159     */
160    @Override
161    public void clean()
162    {
163        super.clean();
164
165        messageId = -1;
166        tlvId = -1;
167        message = null;
168        ldapResult = null;
169        currentControl = null;
170        currentAttribute = null;
171        currentFilter = null;
172        terminalFilter = null;
173        topFilter = null;
174        controlFactory = null;
175        intermediateFactory = null;
176        extendedFactory = null;
177        setDecodedBytes( 0 );
178    }
179
180
181    /**
182     * @return Returns true if the attribute is binary.
183     * @param id checks if an attribute id is binary
184     */
185    public boolean isBinary( String id )
186    {
187        return binaryAttributeDetector.isBinary( id );
188    }
189
190
191    /**
192     * @return The message ID
193     */
194    public int getMessageId()
195    {
196        return messageId;
197    }
198
199
200    /**
201     * Set the message ID
202     * @param messageId the id of the message
203     */
204    public void setMessageId( int messageId )
205    {
206        this.messageId = messageId;
207    }
208
209
210    /**
211     * @return the current control being created
212     */
213    public Control getCurrentControl()
214    {
215        return currentControl;
216    }
217
218
219    /**
220     * Store a newly created control
221     * @param currentControl The control to store
222     */
223    public void setCurrentControl( Control currentControl )
224    {
225        this.currentControl = currentControl;
226    }
227
228
229    /**
230     * Sets the binary attribute detector
231     * 
232     * @param binaryAttributeDetector the binary attribute detector
233     */
234    public void setBinaryAttributeDetector( BinaryAttributeDetector binaryAttributeDetector )
235    {
236        this.binaryAttributeDetector = binaryAttributeDetector;
237    }
238
239
240    /**
241     * @return the binary attribute detector
242     */
243    public BinaryAttributeDetector getBinaryAttributeDetector()
244    {
245        return binaryAttributeDetector;
246    }
247
248
249    /**
250     * @return the ldapResult
251     */
252    public LdapResult getLdapResult()
253    {
254        return ldapResult;
255    }
256
257
258    /**
259     * @param ldapResult the ldapResult to set
260     */
261    public void setLdapResult( LdapResult ldapResult )
262    {
263        this.ldapResult = ldapResult;
264    }
265
266
267    /**
268     * @return the controlFactory
269     */
270    public ControlFactory<?> getControlFactory()
271    {
272        return controlFactory;
273    }
274
275
276    /**
277     * @param controlFactory the controlFactory to set
278     */
279    public void setControlFactory( ControlFactory<?> controlFactory )
280    {
281        this.controlFactory = controlFactory;
282    }
283
284
285    /**
286     * @return the currentAttribute
287     */
288    public Attribute getCurrentAttribute()
289    {
290        return currentAttribute;
291    }
292
293
294    /**
295     * @param currentAttribute the currentAttribute to set
296     */
297    public void setCurrentAttribute( Attribute currentAttribute )
298    {
299        this.currentAttribute = currentAttribute;
300    }
301
302
303    /**
304     * @return the currentModification
305     */
306    public Modification getCurrentModification()
307    {
308        return currentModification;
309    }
310
311
312    /**
313     * @param currentModification the currentModification to set
314     */
315    public void setCurrentModification( Modification currentModification )
316    {
317        this.currentModification = currentModification;
318    }
319
320
321    /**
322     * Set the SearchRequest PDU TLV's Id
323     * @param tlvId The TLV id
324     */
325    public void setTlvId( int tlvId )
326    {
327        this.tlvId = tlvId;
328    }
329
330
331    /**
332     * @return the terminalFilter
333     */
334    public Filter getTerminalFilter()
335    {
336        return terminalFilter;
337    }
338
339
340    /**
341     * @param terminalFilter the terminalFilter to set
342     */
343    public void setTerminalFilter( Filter terminalFilter )
344    {
345        this.terminalFilter = terminalFilter;
346    }
347
348
349    /**
350     * @return the currentFilter
351     */
352    public Filter getCurrentFilter()
353    {
354        return currentFilter;
355    }
356
357
358    /**
359     * @param currentFilter the currentFilter to set
360     */
361    public void setCurrentFilter( Filter currentFilter )
362    {
363        this.currentFilter = currentFilter;
364    }
365
366
367    /**
368     * Add a current filter. We have two cases :
369     * - there is no previous current filter : the filter
370     * is the top level filter
371     * - there is a previous current filter : the filter is added
372     * to the currentFilter set, and the current filter is changed
373     *
374     * In any case, the previous current filter will always be a
375     * ConnectorFilter when this method is called.
376     *
377     * @param localFilter The filter to set.
378     * @throws DecoderException If the filter is invalid
379     */
380    public void addCurrentFilter( Filter localFilter ) throws DecoderException
381    {
382        if ( currentFilter != null )
383        {
384            // Ok, we have a parent. The new Filter will be added to
385            // this parent, and will become the currentFilter if it's a connector.
386            ( ( ConnectorFilter ) currentFilter ).addFilter( localFilter );
387            localFilter.setParent( currentFilter, currentFilter.getTlvId() );
388
389            if ( localFilter instanceof ConnectorFilter )
390            {
391                currentFilter = localFilter;
392            }
393        }
394        else
395        {
396            // No parent. This Filter will become the root.
397            currentFilter = localFilter;
398            currentFilter.setParent( null, tlvId );
399            topFilter = localFilter;
400        }
401    }
402
403
404    /**
405     * This method is used to clear the filter's stack for terminated elements. An element
406     * is considered as terminated either if :
407     *  - it's a final element (ie an element which cannot contains a Filter)
408     *  - its current length equals its expected length.
409     */
410    public void unstackFilters()
411    {
412        TLV tlv = getCurrentTLV();
413        TLV localParent = tlv.getParent();
414        Filter localFilter = terminalFilter;
415
416        // The parent has been completed, so fold it
417        while ( ( localParent != null ) && ( localParent.getExpectedLength() == 0 ) )
418        {
419            int parentTlvId = localFilter.getParent() != null ? localFilter.getParent().getTlvId() : localFilter
420                .getParentTlvId();
421
422            if ( localParent.getId() != parentTlvId )
423            {
424                localParent = localParent.getParent();
425            }
426            else
427            {
428                Filter filterParent = localFilter.getParent();
429
430                // We have a special case with PresentFilter, which has not been
431                // pushed on the stack, so we need to get its parent's parent
432                if ( localFilter instanceof PresentFilter )
433                {
434                    if ( filterParent == null )
435                    {
436                        // We don't have parent, get out
437                        break;
438                    }
439
440                    filterParent = filterParent.getParent();
441                }
442                else
443                {
444                    filterParent = filterParent.getParent();
445                }
446
447                if ( filterParent != null )
448                {
449                    // The parent is a filter ; it will become the new currentFilter
450                    // and we will loop again.
451                    localFilter = currentFilter;
452                    currentFilter = filterParent;
453                    localParent = localParent.getParent();
454                }
455                else
456                {
457                    // We can stop the recursion, we have reached the searchResult Object
458                    break;
459                }
460            }
461        }
462    }
463    
464    
465    /**
466     * Copy the LdapResult element from a opaque response to a newly created 
467     * extendedResponse
468     *  
469     * @param resultResponse The original response
470     * @param extendedResponse The newly created ExtendedResponse
471     */
472    public static void copyLdapResult( ResultResponse resultResponse, ExtendedResponse extendedResponse )
473    {
474        extendedResponse.getLdapResult().setDiagnosticMessage( resultResponse.getLdapResult().getDiagnosticMessage() );
475        extendedResponse.getLdapResult().setMatchedDn( resultResponse.getLdapResult().getMatchedDn() );
476        extendedResponse.getLdapResult().setReferral( resultResponse.getLdapResult().getReferral() );
477        extendedResponse.getLdapResult().setResultCode( resultResponse.getLdapResult().getResultCode() );
478    }
479
480
481    /**
482     * @return the topFilter
483     */
484    public Filter getTopFilter()
485    {
486        return topFilter;
487    }
488
489
490    /**
491     * @param topFilter the topFilter to set
492     */
493    public void setTopFilter( Filter topFilter )
494    {
495        this.topFilter = topFilter;
496    }
497
498
499    /**
500     * @return the tlvId
501     */
502    public int getTlvId()
503    {
504        return tlvId;
505    }
506
507
508    /**
509     * @return the intermediateFactory
510     */
511    public IntermediateOperationFactory getIntermediateFactory()
512    {
513        return intermediateFactory;
514    }
515
516
517    /**
518     * @param intermediateFactory the intermediateFactory to set
519     */
520    public void setIntermediateFactory( IntermediateOperationFactory intermediateFactory )
521    {
522        this.intermediateFactory = intermediateFactory;
523    }
524
525
526    /**
527     * @return the extendedFactory
528     */
529    public ExtendedOperationFactory getExtendedFactory()
530    {
531        return extendedFactory;
532    }
533
534
535    /**
536     * @param extendedFactory the extendedFactory to set
537     */
538    public void setExtendedFactory( ExtendedOperationFactory extendedFactory )
539    {
540        this.extendedFactory = extendedFactory;
541    }
542}