TelnetOutputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.net.telnet;

  18. import java.io.IOException;
  19. import java.io.OutputStream;

  20. /**
  21.  * Wraps an output stream.
  22.  * <p>
  23.  * In binary mode, the only conversion is to double IAC.
  24.  * </p>
  25.  * <p>
  26.  * In ASCII mode, if convertCRtoCRLF is true (currently always true), any CR is converted to CRLF. IACs are doubled. Also, a bare LF is converted to CRLF and a
  27.  * bare CR is converted to CR\0
  28.  * </p>
  29.  */
  30. final class TelnetOutputStream extends OutputStream {
  31.     private final TelnetClient client;
  32.     // TODO there does not appear to be any way to change this value - should it be a ctor parameter?
  33.     private final boolean convertCRtoCRLF = true;
  34.     private boolean lastWasCR;

  35.     TelnetOutputStream(final TelnetClient client) {
  36.         this.client = client;
  37.     }

  38.     /** Closes the stream. */
  39.     @Override
  40.     public void close() throws IOException {
  41.         client.closeOutputStream();
  42.     }

  43.     /** Flushes the stream. */
  44.     @Override
  45.     public void flush() throws IOException {
  46.         client.flushOutputStream();
  47.     }

  48.     /**
  49.      * Writes a byte array to the stream.
  50.      *
  51.      * @param buffer The byte array to write.
  52.      * @throws IOException If an error occurs while writing to the underlying stream.
  53.      */
  54.     @Override
  55.     public void write(final byte buffer[]) throws IOException {
  56.         write(buffer, 0, buffer.length);
  57.     }

  58.     /**
  59.      * Writes a number of bytes from a byte array to the stream starting from a given offset.
  60.      *
  61.      * @param buffer The byte array to write.
  62.      * @param offset The offset into the array at which to start copying data.
  63.      * @param length The number of bytes to write.
  64.      * @throws IOException If an error occurs while writing to the underlying stream.
  65.      */
  66.     @Override
  67.     public void write(final byte buffer[], int offset, int length) throws IOException {
  68.         synchronized (client) {
  69.             while (length-- > 0) {
  70.                 write(buffer[offset++]);
  71.             }
  72.         }
  73.     }

  74.     /**
  75.      * Writes a byte to the stream.
  76.      *
  77.      * @param ch The byte to write.
  78.      * @throws IOException If an error occurs while writing to the underlying stream.
  79.      */
  80.     @Override
  81.     public void write(int ch) throws IOException {

  82.         synchronized (client) {
  83.             ch &= 0xff;

  84.             if (client.requestedWont(TelnetOption.BINARY)) // i.e. ASCII
  85.             {
  86.                 if (lastWasCR) {
  87.                     if (convertCRtoCRLF) {
  88.                         client.sendByte('\n');
  89.                         if (ch == '\n') // i.e. was CRLF anyway
  90.                         {
  91.                             lastWasCR = false;
  92.                             return;
  93.                         }
  94.                     } // __convertCRtoCRLF
  95.                     else if (ch != '\n') {
  96.                         client.sendByte('\0'); // RFC854 requires CR NUL for bare CR
  97.                     }
  98.                 }

  99.                 switch (ch) {
  100.                 case '\r':
  101.                     client.sendByte('\r');
  102.                     lastWasCR = true;
  103.                     break;
  104.                 case '\n':
  105.                     if (!lastWasCR) { // convert LF to CRLF
  106.                         client.sendByte('\r');
  107.                     }
  108.                     client.sendByte(ch);
  109.                     lastWasCR = false;
  110.                     break;
  111.                 case TelnetCommand.IAC:
  112.                     client.sendByte(TelnetCommand.IAC);
  113.                     client.sendByte(TelnetCommand.IAC);
  114.                     lastWasCR = false;
  115.                     break;
  116.                 default:
  117.                     client.sendByte(ch);
  118.                     lastWasCR = false;
  119.                     break;
  120.                 }
  121.             } // end ASCII
  122.             else if (ch == TelnetCommand.IAC) {
  123.                 client.sendByte(ch);
  124.                 client.sendByte(TelnetCommand.IAC);
  125.             } else {
  126.                 client.sendByte(ch);
  127.             }
  128.         }
  129.     }
  130. }