001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.protocols.internal;
028    
029    
030    
031    import java.io.OutputStream;
032    import java.io.IOException;
033    import java.util.ArrayList;
034    
035    import org.opends.messages.Message;
036    import org.opends.server.core.*;
037    import org.opends.server.protocols.asn1.ASN1Element;
038    import org.opends.server.protocols.ldap.*;
039    import org.opends.server.types.AuthenticationType;
040    import org.opends.server.types.Control;
041    import org.opends.server.types.SearchResultEntry;
042    import org.opends.server.types.SearchResultReference;
043    
044    import static org.opends.messages.ProtocolMessages.*;
045    import static org.opends.server.protocols.ldap.LDAPConstants.*;
046    import static org.opends.server.util.ServerConstants.*;
047    
048    
049    
050    /**
051     * This class provides an implementation of a
052     * {@code java.io.OutputStream} that can be used to facilitate
053     * internal communication with the Directory Server.  On the backend,
054     * data written to this output stream will be first decoded as an
055     * ASN.1 element and then as an LDAP message.  That LDAP message will
056     * be converted to an internal operation which will then be processed
057     * and the result returned to the client via the input stream on the
058     * other side of the associated internal LDAP socket.
059     */
060    @org.opends.server.types.PublicAPI(
061         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
062         mayInstantiate=false,
063         mayExtend=false,
064         mayInvoke=true)
065    public final class InternalLDAPOutputStream
066           extends OutputStream
067           implements InternalSearchListener
068    {
069      // Indicates whether this stream has been closed.
070      private boolean closed;
071    
072      // Indicates whether the type of the ASN.1 element is needed.
073      private boolean needType;
074    
075      // The BER type for the ASN.1 element being read.
076      private byte elementType;
077    
078      // The data for the ASN.1 element being read.
079      private byte[] elementBytes;
080    
081      // The length bytes for the ASN.1 element being read.
082      private byte[] lengthBytes;
083    
084      // The offset in the appropriate array at which we should begin
085      // writing data.  This could either refer to the length or data
086      // array, depending on the stage of the encoding process.
087      private int arrayOffset;
088    
089      // The internal LDAP socket with which this output stream is
090      // associated.
091      private InternalLDAPSocket socket;
092    
093    
094    
095      /**
096       * Creates a new instance of an internal LDAP output stream that is
097       * associated with the provided internal LDAP socket.
098       *
099       * @param  socket  The internal LDAP socket that will be serviced by
100       *                 this internal LDAP output stream.
101       */
102      public InternalLDAPOutputStream(InternalLDAPSocket socket)
103      {
104        this.socket = socket;
105    
106        closed = false;
107    
108        needType = true;
109        elementType = 0x00;
110        elementBytes = null;
111        lengthBytes = null;
112        arrayOffset = 0;
113      }
114    
115    
116    
117      /**
118       * Closes this output stream, its associated socket, and the
119       * socket's associated input stream.
120       */
121      @Override()
122      public void close()
123      {
124        socket.close();
125      }
126    
127    
128    
129      /**
130       * Closes this output stream through an internal mechanism that will
131       * not cause an infinite recursion loop by trying to also close the
132       * output stream.
133       */
134      @org.opends.server.types.PublicAPI(
135           stability=org.opends.server.types.StabilityLevel.PRIVATE,
136           mayInstantiate=false,
137           mayExtend=false,
138           mayInvoke=false)
139      void closeInternal()
140      {
141        closed = true;
142      }
143    
144    
145    
146      /**
147       * Flushes this output stream and forces any buffered data to be
148       * written out.  This will have no effect, since this output
149       * stream implementation does not use buffering.
150       */
151      @Override()
152      public void flush()
153      {
154        // No implementation is required.
155      }
156    
157    
158    
159      /**
160       * Writes the contents of the provided byte array to this output
161       * stream.
162       *
163       * @param  b  The byte array to be written.
164       *
165       * @throws  IOException  If the output stream is closed, or if there
166       *                       is a problem with the data being written.
167       */
168      @Override()
169      public void write(byte[] b)
170             throws IOException
171      {
172        write(b, 0, b.length);
173      }
174    
175    
176    
177      /**
178       * Writes the specified portion of the data in the provided byte
179       * array to this output stream.  Any data written will be
180       * accumulated until a complete ASN.1 element is available, at which
181       * point it will be decoded as an LDAP message and converted to an
182       * internal operation that will be processed.
183       *
184       * @param  b    The byte array containing the data to be read.
185       * @param  off  The position in the array at which to start reading
186       *              data.
187       * @param  len  The number of bytes to read from the array.
188       *
189       * @throws  IOException  If the output stream is closed, or if there
190       *                       is a problem with the data being written.
191       */
192      @Override()
193      public synchronized void write(byte[] b, int off, int len)
194             throws IOException
195      {
196        if (closed)
197        {
198          Message m = ERR_INTERNALOS_CLOSED.get();
199          throw new IOException(m.toString());
200        }
201    
202        if (len == 0)
203        {
204          return;
205        }
206    
207    
208        // See if we need to read the BER type.
209        int position  = off;
210        int remaining = len;
211        if (needType)
212        {
213          elementType = b[position++];
214          needType = false;
215    
216          if (--remaining <= 0)
217          {
218            return;
219          }
220        }
221    
222    
223        // See if we need to read the first length byte.
224        if ((lengthBytes == null) && (elementBytes == null))
225        {
226          int length = b[position++];
227          if (length == (length & 0x7F))
228          {
229            // It's a single-byte length, so we can create the value
230            // array.
231            elementBytes = new byte[length];
232          }
233          else
234          {
235            // It's a multi-byte length, so we can create the length
236            // array.
237            lengthBytes = new byte[length & 0x7F];
238          }
239    
240          arrayOffset = 0;
241          if (--remaining <= 0)
242          {
243            return;
244          }
245        }
246    
247    
248        // See if we need to continue reading part of a multi-byte length.
249        if (lengthBytes != null)
250        {
251          // See if we have enough to read the full length.  If so, then
252          // do it.  Otherwise, read what we can and return.
253          int needed = lengthBytes.length - arrayOffset;
254          if (remaining >= needed)
255          {
256            System.arraycopy(b, position, lengthBytes, arrayOffset,
257                             needed);
258            position += needed;
259            remaining -= needed;
260    
261            int length = 0;
262            for (byte lb : lengthBytes)
263            {
264              length <<= 8;
265              length |= (lb & 0xFF);
266            }
267    
268            elementBytes = new byte[length];
269            lengthBytes = null;
270            arrayOffset = 0;
271            if (remaining <= 0)
272            {
273              return;
274            }
275          }
276          else
277          {
278            System.arraycopy(b, position, lengthBytes, arrayOffset,
279                             remaining);
280            arrayOffset += remaining;
281            return;
282          }
283        }
284    
285    
286        // See if we need to read data for the element value.
287        if (elementBytes != null)
288        {
289          // See if we have enough to read the full value.  If so, then
290          // do it, create the element, and process it.  Otherwise, read
291          // what we can and return.
292          int needed = elementBytes.length - arrayOffset;
293          if (remaining >= needed)
294          {
295            System.arraycopy(b, position, elementBytes, arrayOffset,
296                             needed);
297            position += needed;
298            remaining -= needed;
299            processElement(new ASN1Element(elementType, elementBytes));
300    
301            needType     = true;
302            arrayOffset  = 0;
303            lengthBytes  = null;
304            elementBytes = null;
305          }
306          else
307          {
308            System.arraycopy(b, position, lengthBytes, arrayOffset,
309                             remaining);
310            arrayOffset += remaining;
311            return;
312          }
313        }
314    
315    
316        // If there is still more data available, then call this method
317        // again to process it.
318        if (remaining > 0)
319        {
320          write(b, position, remaining);
321        }
322      }
323    
324    
325    
326      /**
327       * Writes a single byte of data to this output stream.  If the byte
328       * written completes an ASN.1 element that was in progress, then it
329       * will be decoded as an LDAP message and converted to an internal
330       * operation that will be processed.  Otherwise, the data will be
331       * accumulated until a complete element can be formed.
332       *
333       * @param  b The byte to be written.
334       *
335       * @throws  IOException  If the output stream is closed, or if there
336       *                       is a problem with the data being written.
337       */
338      @Override()
339      public synchronized void write(int b)
340             throws IOException
341      {
342        if (closed)
343        {
344          Message m = ERR_INTERNALOS_CLOSED.get();
345          throw new IOException(m.toString());
346        }
347    
348        if (needType)
349        {
350          elementType = (byte) (b & 0xFF);
351          needType = false;
352          return;
353        }
354        else if (elementBytes != null)
355        {
356          // The byte should be part of the element value.
357          elementBytes[arrayOffset++] = (byte) (b & 0xFF);
358          if (arrayOffset == elementBytes.length)
359          {
360            // The element has been completed, so process it.
361            processElement(new ASN1Element(elementType, elementBytes));
362          }
363    
364          lengthBytes  = null;
365          elementBytes = null;
366          arrayOffset  = 0;
367          needType     = true;
368    
369          return;
370        }
371        else if (lengthBytes != null)
372        {
373          // The byte should be part of a multi-byte length.
374          lengthBytes[arrayOffset++] = (byte) (b & 0xFF);
375          if (arrayOffset == lengthBytes.length)
376          {
377            int length = 0;
378            for (int i=0; i < lengthBytes.length; i++)
379            {
380              length <<= 8;
381              length |= (lengthBytes[i] & 0xFF);
382            }
383    
384            elementBytes = new byte[length];
385            lengthBytes  = null;
386            arrayOffset   = 0;
387          }
388    
389          return;
390        }
391        else
392        {
393          if ((b & 0x7F) == b)
394          {
395            // It's the complete length.
396            elementBytes = new byte[b];
397            lengthBytes  = null;
398            arrayOffset = 0;
399          }
400          else
401          {
402            lengthBytes  = new byte[b & 0x7F];
403            elementBytes = null;
404            arrayOffset  = 0;
405          }
406    
407          return;
408        }
409      }
410    
411    
412    
413      /**
414       * Processes the provided ASN.1 element by decoding it as an LDAP
415       * message, converting that to an internal operation, and sending
416       * the appropriate response message(s) to the client through the
417       * corresponding internal LDAP input stream.
418       *
419       * @param  element  The ASN.1 element to be processed.
420       *
421       * @throws  IOException  If a problem occurs while attempting to
422       *                       decode the provided ASN.1 element as an
423       *                       LDAP message.
424       */
425      private void processElement(ASN1Element element)
426              throws IOException
427      {
428        LDAPMessage message;
429        try
430        {
431          message = LDAPMessage.decode(element.decodeAsSequence());
432        }
433        catch (Exception e)
434        {
435          throw new IOException(e.getMessage());
436        }
437    
438        switch (message.getProtocolOpType())
439        {
440          case OP_TYPE_ABANDON_REQUEST:
441            // No action is required.
442            return;
443    
444          case OP_TYPE_ADD_REQUEST:
445            processAddOperation(message);
446            break;
447    
448          case OP_TYPE_BIND_REQUEST:
449            processBindOperation(message);
450            break;
451    
452          case OP_TYPE_COMPARE_REQUEST:
453            processCompareOperation(message);
454            break;
455    
456    
457          case OP_TYPE_DELETE_REQUEST:
458            processDeleteOperation(message);
459            break;
460    
461    
462          case OP_TYPE_EXTENDED_REQUEST:
463            processExtendedOperation(message);
464            break;
465    
466    
467          case OP_TYPE_MODIFY_REQUEST:
468            processModifyOperation(message);
469            break;
470    
471    
472          case OP_TYPE_MODIFY_DN_REQUEST:
473            processModifyDNOperation(message);
474            break;
475    
476    
477          case OP_TYPE_SEARCH_REQUEST:
478            processSearchOperation(message);
479            break;
480    
481    
482          case OP_TYPE_UNBIND_REQUEST:
483            socket.close();
484            break;
485    
486    
487          default:
488            Message m = ERR_INTERNALOS_INVALID_REQUEST.get(
489                             message.getProtocolElementName());
490            throw new IOException(m.toString());
491        }
492      }
493    
494    
495    
496      /**
497       * Processes the content of the provided LDAP message as an add
498       * operation and returns the appropriate result to the client.
499       *
500       * @param  message  The LDAP message containing the request to
501       *                  process.
502       *
503       * @throws  IOException  If a problem occurs while attempting to
504       *                       process the operation.
505       */
506      private void processAddOperation(LDAPMessage message)
507              throws IOException
508      {
509        int messageID = message.getMessageID();
510        AddRequestProtocolOp request = message.getAddRequestProtocolOp();
511    
512        ArrayList<Control> requestControls = new ArrayList<Control>();
513        if (message.getControls() != null)
514        {
515          for (LDAPControl c : message.getControls())
516          {
517            requestControls.add(c.getControl());
518          }
519        }
520    
521        InternalClientConnection conn = socket.getConnection();
522        AddOperationBasis op =
523             new AddOperationBasis(conn, conn.nextOperationID(),
524                                   messageID, requestControls,
525                                   request.getDN(),
526                                   request.getAttributes());
527        op.run();
528    
529        AddResponseProtocolOp addResponse =
530             new AddResponseProtocolOp(op.getResultCode().getIntValue(),
531                                       op.getErrorMessage().toMessage(),
532                                       op.getMatchedDN(),
533                                       op.getReferralURLs());
534        ArrayList<LDAPControl> responseControls =
535             new ArrayList<LDAPControl>();
536        for (Control c : op.getResponseControls())
537        {
538          responseControls.add(new LDAPControl(c));
539        }
540    
541        socket.getInputStream().addLDAPMessage(
542             new LDAPMessage(messageID, addResponse, responseControls));
543      }
544    
545    
546    
547      /**
548       * Processes the content of the provided LDAP message as a bind
549       * operation and returns the appropriate result to the client.
550       *
551       * @param  message  The LDAP message containing the request to
552       *                  process.
553       *
554       * @throws  IOException  If a problem occurs while attempting to
555       *                       process the operation.
556       */
557      private void processBindOperation(LDAPMessage message)
558              throws IOException
559      {
560        int messageID = message.getMessageID();
561        BindRequestProtocolOp request =
562             message.getBindRequestProtocolOp();
563    
564        if (request.getAuthenticationType() == AuthenticationType.SASL)
565        {
566          Message m = ERR_INTERNALOS_SASL_BIND_NOT_SUPPORTED.get();
567          BindResponseProtocolOp bindResponse =
568               new BindResponseProtocolOp(
569                        LDAPResultCode.UNWILLING_TO_PERFORM, m);
570          socket.getInputStream().addLDAPMessage(
571               new LDAPMessage(messageID, bindResponse));
572          return;
573        }
574    
575        ArrayList<Control> requestControls = new ArrayList<Control>();
576        if (message.getControls() != null)
577        {
578          for (LDAPControl c : message.getControls())
579          {
580            requestControls.add(c.getControl());
581          }
582        }
583    
584        InternalClientConnection conn = socket.getConnection();
585        BindOperationBasis op =
586             new BindOperationBasis(conn, conn.nextOperationID(),
587                      messageID, requestControls,
588                      String.valueOf(request.getProtocolVersion()),
589                      request.getDN(), request.getSimplePassword());
590        op.run();
591    
592        BindResponseProtocolOp bindResponse =
593             new BindResponseProtocolOp(op.getResultCode().getIntValue(),
594                                        op.getErrorMessage().toMessage(),
595                                        op.getMatchedDN(),
596                                        op.getReferralURLs());
597        ArrayList<LDAPControl> responseControls =
598             new ArrayList<LDAPControl>();
599        for (Control c : op.getResponseControls())
600        {
601          responseControls.add(new LDAPControl(c));
602        }
603    
604        if (bindResponse.getResultCode() == LDAPResultCode.SUCCESS)
605        {
606          socket.setConnection(new InternalClientConnection(
607               op.getAuthenticationInfo()));
608        }
609    
610        socket.getInputStream().addLDAPMessage(
611             new LDAPMessage(messageID, bindResponse, responseControls));
612      }
613    
614    
615    
616      /**
617       * Processes the content of the provided LDAP message as a compare
618       * operation and returns the appropriate result to the client.
619       *
620       * @param  message  The LDAP message containing the request to
621       *                  process.
622       *
623       * @throws  IOException  If a problem occurs while attempting to
624       *                       process the operation.
625       */
626      private void processCompareOperation(LDAPMessage message)
627              throws IOException
628      {
629        int messageID = message.getMessageID();
630        CompareRequestProtocolOp request =
631             message.getCompareRequestProtocolOp();
632    
633        ArrayList<Control> requestControls = new ArrayList<Control>();
634        if (message.getControls() != null)
635        {
636          for (LDAPControl c : message.getControls())
637          {
638            requestControls.add(c.getControl());
639          }
640        }
641    
642        InternalClientConnection conn = socket.getConnection();
643        CompareOperationBasis op =
644             new CompareOperationBasis(conn, conn.nextOperationID(),
645                      messageID, requestControls, request.getDN(),
646                      request.getAttributeType(),
647                      request.getAssertionValue());
648        op.run();
649    
650        CompareResponseProtocolOp compareResponse =
651             new CompareResponseProtocolOp(
652                      op.getResultCode().getIntValue(),
653                      op.getErrorMessage().toMessage(),
654                      op.getMatchedDN(),
655                      op.getReferralURLs());
656        ArrayList<LDAPControl> responseControls =
657             new ArrayList<LDAPControl>();
658        for (Control c : op.getResponseControls())
659        {
660          responseControls.add(new LDAPControl(c));
661        }
662    
663        socket.getInputStream().addLDAPMessage(
664             new LDAPMessage(messageID, compareResponse,
665                             responseControls));
666      }
667    
668    
669    
670      /**
671       * Processes the content of the provided LDAP message as a delete
672       * operation and returns the appropriate result to the client.
673       *
674       * @param  message  The LDAP message containing the request to
675       *                  process.
676       *
677       * @throws  IOException  If a problem occurs while attempting to
678       *                       process the operation.
679       */
680      private void processDeleteOperation(LDAPMessage message)
681              throws IOException
682      {
683        int messageID = message.getMessageID();
684        DeleteRequestProtocolOp request =
685             message.getDeleteRequestProtocolOp();
686    
687        ArrayList<Control> requestControls = new ArrayList<Control>();
688        if (message.getControls() != null)
689        {
690          for (LDAPControl c : message.getControls())
691          {
692            requestControls.add(c.getControl());
693          }
694        }
695    
696        InternalClientConnection conn = socket.getConnection();
697        DeleteOperationBasis op =
698             new DeleteOperationBasis(conn, conn.nextOperationID(),
699                      messageID, requestControls, request.getDN());
700        op.run();
701    
702        DeleteResponseProtocolOp deleteResponse =
703             new DeleteResponseProtocolOp(
704                      op.getResultCode().getIntValue(),
705                      op.getErrorMessage().toMessage(),
706                      op.getMatchedDN(),
707                      op.getReferralURLs());
708        ArrayList<LDAPControl> responseControls =
709             new ArrayList<LDAPControl>();
710        for (Control c : op.getResponseControls())
711        {
712          responseControls.add(new LDAPControl(c));
713        }
714    
715        socket.getInputStream().addLDAPMessage(
716             new LDAPMessage(messageID, deleteResponse,
717                             responseControls));
718      }
719    
720    
721    
722      /**
723       * Processes the content of the provided LDAP message as an extended
724       * operation and returns the appropriate result to the client.
725       *
726       * @param  message  The LDAP message containing the request to
727       *                  process.
728       *
729       * @throws  IOException  If a problem occurs while attempting to
730       *                       process the operation.
731       */
732      private void processExtendedOperation(LDAPMessage message)
733              throws IOException
734      {
735        int messageID = message.getMessageID();
736        ExtendedRequestProtocolOp request =
737             message.getExtendedRequestProtocolOp();
738        if (request.getOID().equals(OID_START_TLS_REQUEST))
739        {
740          Message m = ERR_INTERNALOS_STARTTLS_NOT_SUPPORTED.get();
741          ExtendedResponseProtocolOp extendedResponse =
742               new ExtendedResponseProtocolOp(
743                        LDAPResultCode.UNWILLING_TO_PERFORM, m);
744          socket.getInputStream().addLDAPMessage(
745               new LDAPMessage(messageID, extendedResponse));
746          return;
747        }
748    
749        ArrayList<Control> requestControls = new ArrayList<Control>();
750        if (message.getControls() != null)
751        {
752          for (LDAPControl c : message.getControls())
753          {
754            requestControls.add(c.getControl());
755          }
756        }
757    
758        InternalClientConnection conn = socket.getConnection();
759        ExtendedOperationBasis op =
760             new ExtendedOperationBasis(conn, conn.nextOperationID(),
761                      messageID, requestControls, request.getOID(),
762                      request.getValue());
763        op.run();
764    
765        ExtendedResponseProtocolOp extendedResponse =
766             new ExtendedResponseProtocolOp(
767                      op.getResultCode().getIntValue(),
768                      op.getErrorMessage().toMessage(),
769                      op.getMatchedDN(),
770                      op.getReferralURLs(), op.getResponseOID(),
771                      op.getResponseValue());
772        ArrayList<LDAPControl> responseControls =
773             new ArrayList<LDAPControl>();
774        for (Control c : op.getResponseControls())
775        {
776          responseControls.add(new LDAPControl(c));
777        }
778    
779        socket.getInputStream().addLDAPMessage(
780             new LDAPMessage(messageID, extendedResponse,
781                             responseControls));
782      }
783    
784    
785    
786      /**
787       * Processes the content of the provided LDAP message as a modify
788       * operation and returns the appropriate result to the client.
789       *
790       * @param  message  The LDAP message containing the request to
791       *                  process.
792       *
793       * @throws  IOException  If a problem occurs while attempting to
794       *                       process the operation.
795       */
796      private void processModifyOperation(LDAPMessage message)
797              throws IOException
798      {
799        int messageID = message.getMessageID();
800        ModifyRequestProtocolOp request =
801             message.getModifyRequestProtocolOp();
802    
803        ArrayList<Control> requestControls = new ArrayList<Control>();
804        if (message.getControls() != null)
805        {
806          for (LDAPControl c : message.getControls())
807          {
808            requestControls.add(c.getControl());
809          }
810        }
811    
812        InternalClientConnection conn = socket.getConnection();
813        ModifyOperationBasis op =
814             new ModifyOperationBasis(conn, conn.nextOperationID(),
815                      messageID, requestControls, request.getDN(),
816                      request.getModifications());
817        op.run();
818    
819        ModifyResponseProtocolOp modifyResponse =
820             new ModifyResponseProtocolOp(
821                      op.getResultCode().getIntValue(),
822                      op.getErrorMessage().toMessage(),
823                      op.getMatchedDN(),
824                      op.getReferralURLs());
825        ArrayList<LDAPControl> responseControls =
826             new ArrayList<LDAPControl>();
827        for (Control c : op.getResponseControls())
828        {
829          responseControls.add(new LDAPControl(c));
830        }
831    
832        socket.getInputStream().addLDAPMessage(
833             new LDAPMessage(messageID, modifyResponse,
834                             responseControls));
835      }
836    
837    
838    
839      /**
840       * Processes the content of the provided LDAP message as a modify DN
841       * operation and returns the appropriate result to the client.
842       *
843       * @param  message  The LDAP message containing the request to
844       *                  process.
845       *
846       * @throws  IOException  If a problem occurs while attempting to
847       *                       process the operation.
848       */
849      private void processModifyDNOperation(LDAPMessage message)
850              throws IOException
851      {
852        int messageID = message.getMessageID();
853        ModifyDNRequestProtocolOp request =
854             message.getModifyDNRequestProtocolOp();
855    
856        ArrayList<Control> requestControls = new ArrayList<Control>();
857        if (message.getControls() != null)
858        {
859          for (LDAPControl c : message.getControls())
860          {
861            requestControls.add(c.getControl());
862          }
863        }
864    
865        InternalClientConnection conn = socket.getConnection();
866        ModifyDNOperationBasis op =
867             new ModifyDNOperationBasis(conn, conn.nextOperationID(),
868                      messageID, requestControls, request.getEntryDN(),
869                      request.getNewRDN(), request.deleteOldRDN(),
870                      request.getNewSuperior());
871        op.run();
872    
873        ModifyDNResponseProtocolOp modifyDNResponse =
874             new ModifyDNResponseProtocolOp(
875                      op.getResultCode().getIntValue(),
876                      op.getErrorMessage().toMessage(),
877                      op.getMatchedDN(),
878                      op.getReferralURLs());
879        ArrayList<LDAPControl> responseControls =
880             new ArrayList<LDAPControl>();
881        for (Control c : op.getResponseControls())
882        {
883          responseControls.add(new LDAPControl(c));
884        }
885    
886        socket.getInputStream().addLDAPMessage(
887             new LDAPMessage(messageID, modifyDNResponse,
888                             responseControls));
889      }
890    
891    
892    
893      /**
894       * Processes the content of the provided LDAP message as a search
895       * operation and returns the appropriate result to the client.
896       *
897       * @param  message  The LDAP message containing the request to
898       *                  process.
899       *
900       * @throws  IOException  If a problem occurs while attempting to
901       *                       process the operation.
902       */
903      private void processSearchOperation(LDAPMessage message)
904              throws IOException
905      {
906        int messageID = message.getMessageID();
907        SearchRequestProtocolOp request =
908             message.getSearchRequestProtocolOp();
909    
910        ArrayList<Control> requestControls = new ArrayList<Control>();
911        if (message.getControls() != null)
912        {
913          for (LDAPControl c : message.getControls())
914          {
915            requestControls.add(c.getControl());
916          }
917        }
918    
919        InternalClientConnection conn = socket.getConnection();
920        InternalSearchOperation op =
921             new InternalSearchOperation(conn, conn.nextOperationID(),
922                      messageID, requestControls, request.getBaseDN(),
923                      request.getScope(), request.getDereferencePolicy(),
924                      request.getSizeLimit(), request.getTimeLimit(),
925                      request.getTypesOnly(), request.getFilter(),
926                      request.getAttributes(), this);
927        op.run();
928    
929        SearchResultDoneProtocolOp searchDone =
930             new SearchResultDoneProtocolOp(
931                      op.getResultCode().getIntValue(),
932                      op.getErrorMessage().toMessage(),
933                      op.getMatchedDN(),
934                      op.getReferralURLs());
935        ArrayList<LDAPControl> responseControls =
936             new ArrayList<LDAPControl>();
937        for (Control c : op.getResponseControls())
938        {
939          responseControls.add(new LDAPControl(c));
940        }
941    
942        socket.getInputStream().addLDAPMessage(
943             new LDAPMessage(messageID, searchDone, responseControls));
944      }
945    
946    
947    
948      /**
949       * Performs any processing necessary for the provided search result
950       * entry.
951       *
952       * @param  searchOperation  The internal search operation being
953       *                          processed.
954       * @param  searchEntry      The matching search result entry to be
955       *                          processed.
956       */
957      @org.opends.server.types.PublicAPI(
958           stability=org.opends.server.types.StabilityLevel.PRIVATE,
959           mayInstantiate=false,
960           mayExtend=false,
961           mayInvoke=false)
962      public void handleInternalSearchEntry(
963                       InternalSearchOperation searchOperation,
964                       SearchResultEntry searchEntry)
965      {
966        ArrayList<LDAPControl> entryControls =
967             new ArrayList<LDAPControl>();
968        for (Control c : searchEntry.getControls())
969        {
970          entryControls.add(new LDAPControl(c));
971        }
972    
973        SearchResultEntryProtocolOp entry =
974             new SearchResultEntryProtocolOp(searchEntry);
975    
976        socket.getInputStream().addLDAPMessage(
977             new LDAPMessage(searchOperation.getMessageID(), entry,
978                             entryControls));
979      }
980    
981    
982    
983      /**
984       * Performs any processing necessary for the provided search result
985       * reference.
986       *
987       * @param  searchOperation  The internal search operation being
988       *                          processed.
989       * @param  searchReference  The search result reference to be
990       *                          processed.
991       */
992      @org.opends.server.types.PublicAPI(
993           stability=org.opends.server.types.StabilityLevel.PRIVATE,
994           mayInstantiate=false,
995           mayExtend=false,
996           mayInvoke=false)
997      public void handleInternalSearchReference(
998                       InternalSearchOperation searchOperation,
999                       SearchResultReference searchReference)
1000      {
1001        ArrayList<LDAPControl> entryControls =
1002             new ArrayList<LDAPControl>();
1003        for (Control c : searchReference.getControls())
1004        {
1005          entryControls.add(new LDAPControl(c));
1006        }
1007    
1008        SearchResultReferenceProtocolOp reference =
1009             new SearchResultReferenceProtocolOp(searchReference);
1010    
1011        socket.getInputStream().addLDAPMessage(
1012             new LDAPMessage(searchOperation.getMessageID(), reference,
1013                             entryControls));
1014      }
1015    
1016    
1017    
1018      /**
1019       * Retrieves a string representation of this internal LDAP socket.
1020       *
1021       * @return  A string representation of this internal LDAP socket.
1022       */
1023      @Override()
1024      public String toString()
1025      {
1026        return "InternalLDAPOutputStream";
1027      }
1028    }
1029