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 java.nio.ByteBuffer;
024import java.util.Iterator;
025import java.util.Map;
026
027import org.apache.directory.api.asn1.EncoderException;
028import org.apache.directory.api.asn1.ber.tlv.BerValue;
029import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
030import org.apache.directory.api.asn1.util.Asn1Buffer;
031import org.apache.directory.api.i18n.I18n;
032import org.apache.directory.api.ldap.codec.factory.AbandonRequestFactory;
033import org.apache.directory.api.ldap.codec.factory.AddRequestFactory;
034import org.apache.directory.api.ldap.codec.factory.AddResponseFactory;
035import org.apache.directory.api.ldap.codec.factory.BindRequestFactory;
036import org.apache.directory.api.ldap.codec.factory.BindResponseFactory;
037import org.apache.directory.api.ldap.codec.factory.CompareRequestFactory;
038import org.apache.directory.api.ldap.codec.factory.CompareResponseFactory;
039import org.apache.directory.api.ldap.codec.factory.DeleteRequestFactory;
040import org.apache.directory.api.ldap.codec.factory.DeleteResponseFactory;
041import org.apache.directory.api.ldap.codec.factory.ExtendedRequestFactory;
042import org.apache.directory.api.ldap.codec.factory.ExtendedResponseFactory;
043import org.apache.directory.api.ldap.codec.factory.IntermediateResponseFactory;
044import org.apache.directory.api.ldap.codec.factory.ModifyDnRequestFactory;
045import org.apache.directory.api.ldap.codec.factory.ModifyDnResponseFactory;
046import org.apache.directory.api.ldap.codec.factory.ModifyRequestFactory;
047import org.apache.directory.api.ldap.codec.factory.ModifyResponseFactory;
048import org.apache.directory.api.ldap.codec.factory.SearchRequestFactory;
049import org.apache.directory.api.ldap.codec.factory.SearchResultDoneFactory;
050import org.apache.directory.api.ldap.codec.factory.SearchResultEntryFactory;
051import org.apache.directory.api.ldap.codec.factory.SearchResultReferenceFactory;
052import org.apache.directory.api.ldap.codec.factory.UnbindRequestFactory;
053import org.apache.directory.api.ldap.model.message.Control;
054import org.apache.directory.api.ldap.model.message.Message;
055import org.apache.directory.api.ldap.model.message.Request;
056
057
058/**
059 * LDAP BER encoder.
060 *
061 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
062 */
063public final class LdapEncoder
064{
065    /**
066     * Make this final class impossible to instaciate from teh outside
067     */
068    private LdapEncoder()
069    {
070        // Nothing to do
071    }
072
073
074    /**
075     * Encode a control to a byte[]. The controls are encoded recursively, to start with the last
076     * control first.
077     * <br>
078     * A control is encoded as:
079     * <pre>
080     * 0x30 LL
081     *   0x04 LL abcd               control OID
082     *   [0x01 0x01 0x00/0xFF]      control criticality
083     *   [0x04 LL value]            control value
084     * </pre>
085     *
086     *
087     * @param buffer The buffer that will contain the encoded control
088     * @param codec The LdapApiService instance 
089     * @param controls The controls to encode
090     * @param iterator The Iterator instance we are processing
091     * @param isRequest A flag set to <tt>true</tt> if the LdapMessage is a request
092     * @throws EncoderException If the encoding failed
093     */
094    private static void encodeControls( Asn1Buffer buffer, LdapApiService codec,
095        Map<String, Control> controls, Iterator<String> iterator, boolean isRequest ) throws EncoderException
096    {
097        if ( iterator.hasNext() )
098        {
099            // Get the Control from its OID
100            Control control = controls.get( iterator.next() );
101
102            // Encode the remaining controls recursively
103            encodeControls( buffer, codec, controls, iterator, isRequest );
104
105            // Fetch the control's factory from the LdapApiService
106            ControlFactory<?> controlFactory;
107
108            if ( isRequest )
109            {
110                controlFactory = codec.getRequestControlFactories().get( control.getOid() );
111            }
112            else
113            {
114                controlFactory = codec.getResponseControlFactories().get( control.getOid() );
115            }
116
117            if ( controlFactory == null )
118            {
119                throw new EncoderException( I18n.err( I18n.ERR_08002_CANNOT_FIND_CONTROL_FACTORY, control.getOid() ) );
120            }
121
122            int start = buffer.getPos();
123
124            // the value, if any
125            controlFactory.encodeValue( buffer, control );
126            
127            if ( buffer.getPos() != start )
128            {
129                // The control value sequence, as an OctetString
130                BerValue.encodeSequence( buffer, ( byte ) UniversalTag.OCTET_STRING.getValue(), start );
131            }
132
133            // The criticality
134            if ( control.isCritical() )
135            {
136                BerValue.encodeBoolean( buffer, control.isCritical() );
137            }
138
139            // The OID
140            BerValue.encodeOctetString( buffer, control.getOid() );
141
142            // The Control Sequence
143            BerValue.encodeSequence( buffer, start );
144        }
145    }
146
147
148    /**
149     * Encode the protocolOp part of a message
150     *
151     * @param buffer The buffer that will contain the encoded control
152     * @param codec The LdapApiService instance
153     * @param message The message to encode
154     */
155    private static void encodeProtocolOp( Asn1Buffer buffer, LdapApiService codec, Message message )
156    {
157        switch ( message.getType() )
158        {
159            case ABANDON_REQUEST :
160                AbandonRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
161                return;
162
163            case ADD_REQUEST :
164                AddRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
165                return;
166
167            case ADD_RESPONSE:
168                AddResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
169                return;
170
171            case BIND_REQUEST :
172                BindRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
173                return;
174
175            case BIND_RESPONSE :
176                BindResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
177                return;
178
179            case COMPARE_REQUEST :
180                CompareRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
181                return;
182
183            case COMPARE_RESPONSE :
184                CompareResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
185                return;
186
187            case DEL_REQUEST :
188                DeleteRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
189                return;
190
191            case DEL_RESPONSE :
192                DeleteResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
193                return;
194                
195            case EXTENDED_REQUEST :
196                ExtendedRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
197                return;
198                
199            case EXTENDED_RESPONSE :
200                ExtendedResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
201                return;
202                
203            case INTERMEDIATE_RESPONSE :
204                IntermediateResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
205                return;
206
207            case MODIFY_REQUEST :
208                ModifyRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
209                return;
210
211            case MODIFY_RESPONSE :
212                ModifyResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
213                return;
214
215            case MODIFYDN_REQUEST :
216                ModifyDnRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
217                return;
218
219            case MODIFYDN_RESPONSE :
220                ModifyDnResponseFactory.INSTANCE.encodeReverse( codec, buffer, message );
221                return;
222
223            case SEARCH_REQUEST :
224                SearchRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
225                return;
226
227            case SEARCH_RESULT_DONE :
228                SearchResultDoneFactory.INSTANCE.encodeReverse( codec, buffer, message );
229                return;
230
231            case SEARCH_RESULT_ENTRY :
232                SearchResultEntryFactory.INSTANCE.encodeReverse( codec, buffer, message );
233                return;
234
235            case SEARCH_RESULT_REFERENCE :
236                SearchResultReferenceFactory.INSTANCE.encodeReverse( codec, buffer, message );
237                return;
238
239            case UNBIND_REQUEST :
240                UnbindRequestFactory.INSTANCE.encodeReverse( codec, buffer, message );
241                return;
242
243            default:
244                // Nothing to do
245        }
246    }
247
248
249    /**
250     * Generate the PDU which contains the encoded object.
251     *
252     * The generation is done in two phases :
253     * - first, we compute the length of each part and the
254     * global PDU length
255     * - second, we produce the PDU.
256     *
257     * <pre>
258     * 0x30 L1
259     *   |
260     *   +--&gt; 0x02 L2 MessageId
261     *   +--&gt; ProtocolOp
262     *   +--&gt; Controls
263     *
264     * L2 = Length(MessageId)
265     * L1 = Length(0x02) + Length(L2) + L2 + Length(ProtocolOp) + Length(Controls)
266     * LdapMessageLength = Length(0x30) + Length(L1) + L1
267     * </pre>
268     *
269     * @param buffer The Asn1Buffer instance in which we store the temporary result
270     * @param codec The LdapApiService instance
271     * @param message The message to encode
272     * @return A ByteBuffer that contains the PDU
273     * @throws EncoderException If anything goes wrong.
274     */
275    public static ByteBuffer encodeMessage( Asn1Buffer buffer, LdapApiService codec, Message message ) throws EncoderException
276    {
277        int start = buffer.getPos();
278
279        // The controls, if any
280        Map<String, Control> controls = message.getControls();
281
282        if ( ( controls != null ) && ( controls.size() > 0 ) )
283        {
284            encodeControls( buffer, codec, controls, controls.keySet().iterator(), message instanceof Request );
285
286            // The controls tag
287            BerValue.encodeSequence( buffer, ( byte ) LdapCodecConstants.CONTROLS_TAG, start );
288        }
289
290        // The protocolOp part
291        encodeProtocolOp( buffer, codec, message );
292
293        // The message Id
294        BerValue.encodeInteger( buffer, message.getMessageId() );
295
296        // The LdapMessage Sequence
297        BerValue.encodeSequence( buffer );
298
299        return buffer.getBytes();
300    }
301}