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.api.ldap.codec.decorators;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025
026import org.apache.directory.api.asn1.EncoderException;
027import org.apache.directory.api.asn1.ber.tlv.BerValue;
028import org.apache.directory.api.asn1.ber.tlv.TLV;
029import org.apache.directory.api.i18n.I18n;
030import org.apache.directory.api.ldap.codec.api.Decorator;
031import org.apache.directory.api.ldap.codec.api.LdapApiService;
032import org.apache.directory.api.ldap.codec.api.LdapEncoder;
033import org.apache.directory.api.ldap.model.message.LdapResult;
034import org.apache.directory.api.ldap.model.message.Referral;
035import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
036import org.apache.directory.api.ldap.model.name.Dn;
037import org.apache.directory.api.util.Strings;
038
039
040/**
041 * A decorator for the LdapResultResponse message
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045public class LdapResultDecorator implements LdapResult, Decorator<LdapResult>
046{
047    /** The decorated LdapResult */
048    private final LdapResult decoratedLdapResult;
049
050    /** Temporary storage for message bytes */
051    private byte[] errorMessageBytes;
052
053    /** Temporary storage of the byte[] representing the matchedDN */
054    private byte[] matchedDnBytes;
055
056    /** The codec responsible for encoding and decoding this object. */
057    private LdapApiService codec;
058
059    private static final byte[] DEFAULT_SUCCESS = new byte[]
060        { 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00 };
061
062
063    /**
064     * Makes a LdapResult encodable.
065     *
066     * @param decoratedLdapResult the decorated LdapResult
067     */
068    public LdapResultDecorator( LdapResult decoratedLdapResult )
069    {
070        this.decoratedLdapResult = decoratedLdapResult;
071    }
072
073
074    /**
075     * @return The encoded Error message
076     */
077    public byte[] getErrorMessageBytes()
078    {
079        return errorMessageBytes;
080    }
081
082
083    /**
084     * Set the encoded message's bytes
085     * @param errorMessageBytes The encoded bytes
086     */
087    public void setErrorMessageBytes( byte[] errorMessageBytes )
088    {
089        this.errorMessageBytes = errorMessageBytes;
090    }
091
092
093    /**
094     * Sets the encoded value for MatchedDn
095     *
096     * @param matchedDnBytes The encoded MatchedDN
097     */
098    public void setMatchedDnBytes( byte[] matchedDnBytes )
099    {
100        this.matchedDnBytes = matchedDnBytes;
101    }
102
103
104    /**
105     * @return the encoded MatchedDN
106     */
107    public byte[] getMatchedDnBytes()
108    {
109        return matchedDnBytes;
110    }
111
112
113    //-------------------------------------------------------------------------
114    // The LdapResult methods
115    //-------------------------------------------------------------------------
116
117    /**
118     * {@inheritDoc}
119     */
120    public ResultCodeEnum getResultCode()
121    {
122        return decoratedLdapResult.getResultCode();
123    }
124
125
126    /**
127     * {@inheritDoc}
128     */
129    public void setResultCode( ResultCodeEnum resultCode )
130    {
131        decoratedLdapResult.setResultCode( resultCode );
132    }
133
134
135    /**
136     * {@inheritDoc}
137     */
138    public Dn getMatchedDn()
139    {
140        return decoratedLdapResult.getMatchedDn();
141    }
142
143
144    /**
145     * {@inheritDoc}
146     */
147    public void setMatchedDn( Dn dn )
148    {
149        decoratedLdapResult.setMatchedDn( dn );
150    }
151
152
153    /**
154     * {@inheritDoc}
155     */
156    public String getDiagnosticMessage()
157    {
158        return decoratedLdapResult.getDiagnosticMessage();
159    }
160
161
162    /**
163     * {@inheritDoc}
164     */
165    public void setDiagnosticMessage( String diagnosticMessage )
166    {
167        decoratedLdapResult.setDiagnosticMessage( diagnosticMessage );
168    }
169
170
171    /**
172     * {@inheritDoc}
173     */
174    public boolean isReferral()
175    {
176        return decoratedLdapResult.isReferral();
177    }
178
179
180    /**
181     * {@inheritDoc}
182     */
183    public Referral getReferral()
184    {
185        return decoratedLdapResult.getReferral();
186    }
187
188
189    /**
190     * {@inheritDoc}
191     */
192    public void setReferral( Referral referral )
193    {
194        decoratedLdapResult.setReferral( referral );
195    }
196
197
198    /**
199     * {@inheritDoc}
200     */
201    public String toString()
202    {
203        return decoratedLdapResult.toString();
204    }
205
206
207    //-------------------------------------------------------------------------
208    // The Decorator methods
209    //-------------------------------------------------------------------------
210    /**
211     * Compute the LdapResult length 
212     * 
213     * LdapResult : 
214     *   0x0A 01 resultCode (0..80)
215     *   0x04 L1 matchedDN (L1 = Length(matchedDN)) 
216     *   0x04 L2 errorMessage (L2 = Length(errorMessage)) 
217     *   [0x83 L3] referrals 
218     *     | 
219     *     +--> 0x04 L4 referral 
220     *     +--> 0x04 L5 referral 
221     *     +--> ... 
222     *     +--> 0x04 Li referral 
223     *     +--> ... 
224     *     +--> 0x04 Ln referral 
225     *     
226     * L1 = Length(matchedDN) 
227     * L2 = Length(errorMessage) 
228     * L3 = n*Length(0x04) + sum(Length(L4) .. Length(Ln)) + sum(L4..Ln) 
229     * L4..n = Length(0x04) + Length(Li) + Li 
230     * Length(LdapResult) = Length(0x0x0A) +
231     *      Length(0x01) + 1 + Length(0x04) + Length(L1) + L1 + Length(0x04) +
232     *      Length(L2) + L2 + Length(0x83) + Length(L3) + L3
233     */
234    public int computeLength()
235    {
236        if ( decoratedLdapResult.isDefaultSuccess() )
237        {
238            // The length of a default success PDU : 0xA0 0x01 0x00 0x04 0x00 0x04 0x00
239            return DEFAULT_SUCCESS.length;
240        }
241
242        int ldapResultLength = 0;
243
244        // The result code
245        ldapResultLength = 1 + 1 + BerValue.getNbBytes( getResultCode().getValue() );
246
247        // The matchedDN length
248        if ( getMatchedDn() == null )
249        {
250            ldapResultLength += 1 + 1;
251        }
252        else
253        {
254            byte[] matchedDNBytes = Strings.getBytesUtf8Ascii( Strings.trimLeft( getMatchedDn().getName() ) );
255            ldapResultLength += 1 + TLV.getNbBytes( matchedDNBytes.length ) + matchedDNBytes.length;
256            setMatchedDnBytes( matchedDNBytes );
257        }
258
259        // The errorMessage length
260        byte[] errorMessageBytes = Strings.getBytesUtf8Ascii( getDiagnosticMessage() );
261        ldapResultLength += 1 + TLV.getNbBytes( errorMessageBytes.length ) + errorMessageBytes.length;
262        setErrorMessageBytes( errorMessageBytes );
263
264        int referralLength = LdapEncoder.computeReferralLength( getReferral() );
265
266        if ( referralLength != 0 )
267        {
268            // The referrals
269            ldapResultLength += 1 + TLV.getNbBytes( referralLength ) + referralLength;
270        }
271
272        return ldapResultLength;
273    }
274
275
276    /**
277     * Encode the LdapResult message to a PDU.
278     * 
279     * @param buffer The buffer where to put the PDU
280     * @return The PDU.
281     */
282    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
283    {
284        if ( buffer == null )
285        {
286            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
287        }
288
289        if ( decoratedLdapResult.isDefaultSuccess() )
290        {
291            // The length of a default success PDU : 0xA0 0x01 0x00 0x04 0x00 0x04 0x00
292            buffer.put( DEFAULT_SUCCESS );
293
294            return buffer;
295        }
296
297        try
298        {
299            // The result code
300            BerValue.encodeEnumerated( buffer, getResultCode().getValue() );
301        }
302        catch ( BufferOverflowException boe )
303        {
304            throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
305        }
306
307        // The matchedDN
308        BerValue.encode( buffer, getMatchedDnBytes() );
309
310        // The error message
311        BerValue.encode( buffer, getErrorMessageBytes() );
312
313        // The referrals, if any
314        Referral referral = getReferral();
315
316        if ( referral != null )
317        {
318            LdapEncoder.encodeReferral( buffer, referral );
319        }
320
321        return buffer;
322    }
323
324
325    /**
326     * {@inheritDoc}
327     */
328    public LdapResult getDecorated()
329    {
330        return decoratedLdapResult;
331    }
332
333
334    /**
335     * {@inheritDoc}
336     */
337    public LdapApiService getCodecService()
338    {
339        return codec;
340    }
341
342
343    /**
344     * {@inheritDoc}
345     */
346    public boolean isDefaultSuccess()
347    {
348        return decoratedLdapResult.isDefaultSuccess();
349    }
350}