View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    https://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.codec.api;
21  
22  
23  import java.nio.ByteBuffer;
24  import java.util.Iterator;
25  import java.util.Map;
26  
27  import org.apache.directory.api.asn1.EncoderException;
28  import org.apache.directory.api.asn1.ber.tlv.BerValue;
29  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
30  import org.apache.directory.api.asn1.util.Asn1Buffer;
31  import org.apache.directory.api.i18n.I18n;
32  import org.apache.directory.api.ldap.codec.factory.AbandonRequestFactory;
33  import org.apache.directory.api.ldap.codec.factory.AddRequestFactory;
34  import org.apache.directory.api.ldap.codec.factory.AddResponseFactory;
35  import org.apache.directory.api.ldap.codec.factory.BindRequestFactory;
36  import org.apache.directory.api.ldap.codec.factory.BindResponseFactory;
37  import org.apache.directory.api.ldap.codec.factory.CompareRequestFactory;
38  import org.apache.directory.api.ldap.codec.factory.CompareResponseFactory;
39  import org.apache.directory.api.ldap.codec.factory.DeleteRequestFactory;
40  import org.apache.directory.api.ldap.codec.factory.DeleteResponseFactory;
41  import org.apache.directory.api.ldap.codec.factory.ExtendedRequestFactory;
42  import org.apache.directory.api.ldap.codec.factory.ExtendedResponseFactory;
43  import org.apache.directory.api.ldap.codec.factory.IntermediateResponseFactory;
44  import org.apache.directory.api.ldap.codec.factory.ModifyDnRequestFactory;
45  import org.apache.directory.api.ldap.codec.factory.ModifyDnResponseFactory;
46  import org.apache.directory.api.ldap.codec.factory.ModifyRequestFactory;
47  import org.apache.directory.api.ldap.codec.factory.ModifyResponseFactory;
48  import org.apache.directory.api.ldap.codec.factory.SearchRequestFactory;
49  import org.apache.directory.api.ldap.codec.factory.SearchResultDoneFactory;
50  import org.apache.directory.api.ldap.codec.factory.SearchResultEntryFactory;
51  import org.apache.directory.api.ldap.codec.factory.SearchResultReferenceFactory;
52  import org.apache.directory.api.ldap.codec.factory.UnbindRequestFactory;
53  import org.apache.directory.api.ldap.model.message.Control;
54  import org.apache.directory.api.ldap.model.message.Message;
55  import org.apache.directory.api.ldap.model.message.Request;
56  
57  
58  /**
59   * LDAP BER encoder.
60   *
61   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
62   */
63  public final class LdapEncoder
64  {
65      /**
66       * Make this final class impossible to instaciate from teh outside
67       */
68      private LdapEncoder()
69      {
70          // Nothing to do
71      }
72  
73  
74      /**
75       * Encode a control to a byte[]. The controls are encoded recursively, to start with the last
76       * control first.
77       * <br>
78       * A control is encoded as:
79       * <pre>
80       * 0x30 LL
81       *   0x04 LL abcd               control OID
82       *   [0x01 0x01 0x00/0xFF]      control criticality
83       *   [0x04 LL value]            control value
84       * </pre>
85       *
86       *
87       * @param buffer The buffer that will contain the encoded control
88       * @param codec The LdapApiService instance 
89       * @param controls The controls to encode
90       * @param iterator The Iterator instance we are processing
91       * @param isRequest A flag set to <tt>true</tt> if the LdapMessage is a request
92       * @throws EncoderException If the encoding failed
93       */
94      private static void encodeControls( Asn1Buffer buffer, LdapApiService codec,
95          Map<String, Control> controls, Iterator<String> iterator, boolean isRequest ) throws EncoderException
96      {
97          if ( iterator.hasNext() )
98          {
99              // 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 }