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 18 package org.apache.commons.net.telnet; 19 20 import java.io.IOException; 21 import java.io.OutputStream; 22 23 /** 24 * Wraps an output stream. 25 * <p> 26 * In binary mode, the only conversion is to double IAC. 27 * </p> 28 * <p> 29 * 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 30 * bare CR is converted to CR\0 31 * </p> 32 */ 33 final class TelnetOutputStream extends OutputStream { 34 private final TelnetClient client; 35 // TODO there does not appear to be any way to change this value - should it be a ctor parameter? 36 private final boolean convertCRtoCRLF = true; 37 private boolean lastWasCR; 38 39 TelnetOutputStream(final TelnetClient client) { 40 this.client = client; 41 } 42 43 /** Closes the stream. */ 44 @Override 45 public void close() throws IOException { 46 client.closeOutputStream(); 47 } 48 49 /** Flushes the stream. */ 50 @Override 51 public void flush() throws IOException { 52 client.flushOutputStream(); 53 } 54 55 /** 56 * Writes a byte array to the stream. 57 * 58 * @param buffer The byte array to write. 59 * @throws IOException If an error occurs while writing to the underlying stream. 60 */ 61 @Override 62 public void write(final byte buffer[]) throws IOException { 63 write(buffer, 0, buffer.length); 64 } 65 66 /** 67 * Writes a number of bytes from a byte array to the stream starting from a given offset. 68 * 69 * @param buffer The byte array to write. 70 * @param offset The offset into the array at which to start copying data. 71 * @param length The number of bytes to write. 72 * @throws IOException If an error occurs while writing to the underlying stream. 73 */ 74 @Override 75 public void write(final byte buffer[], int offset, int length) throws IOException { 76 synchronized (client) { 77 while (length-- > 0) { 78 write(buffer[offset++]); 79 } 80 } 81 } 82 83 /** 84 * Writes a byte to the stream. 85 * 86 * @param ch The byte to write. 87 * @throws IOException If an error occurs while writing to the underlying stream. 88 */ 89 @Override 90 public void write(int ch) throws IOException { 91 92 synchronized (client) { 93 ch &= 0xff; 94 95 if (client.requestedWont(TelnetOption.BINARY)) // i.e. ASCII 96 { 97 if (lastWasCR) { 98 if (convertCRtoCRLF) { 99 client.sendByte('\n'); 100 if (ch == '\n') // i.e. was CRLF anyway 101 { 102 lastWasCR = false; 103 return; 104 } 105 } // __convertCRtoCRLF 106 else if (ch != '\n') { 107 client.sendByte('\0'); // RFC854 requires CR NUL for bare CR 108 } 109 } 110 111 switch (ch) { 112 case '\r': 113 client.sendByte('\r'); 114 lastWasCR = true; 115 break; 116 case '\n': 117 if (!lastWasCR) { // convert LF to CRLF 118 client.sendByte('\r'); 119 } 120 client.sendByte(ch); 121 lastWasCR = false; 122 break; 123 case TelnetCommand.IAC: 124 client.sendByte(TelnetCommand.IAC); 125 client.sendByte(TelnetCommand.IAC); 126 lastWasCR = false; 127 break; 128 default: 129 client.sendByte(ch); 130 lastWasCR = false; 131 break; 132 } 133 } // end ASCII 134 else if (ch == TelnetCommand.IAC) { 135 client.sendByte(ch); 136 client.sendByte(TelnetCommand.IAC); 137 } else { 138 client.sendByte(ch); 139 } 140 } 141 } 142 }