98727277a8b47e6a5397d3ac3e77e131424edce8
[nikiroo-utils.git] / src / jexer / net / TelnetOutputStream.java
1 /**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31 package jexer.net;
32
33 import java.io.OutputStream;
34 import java.io.IOException;
35
36 /**
37 * TelnetOutputStream works with TelnetSocket to perform the telnet protocol.
38 */
39 public final class TelnetOutputStream extends OutputStream {
40
41 /**
42 * The root TelnetSocket that has my telnet protocol state.
43 */
44 private TelnetSocket master;
45
46 /**
47 * The raw socket's OutputStream.
48 */
49 private OutputStream output;
50
51 /**
52 * Package private constructor.
53 *
54 * @param master the master TelnetSocket
55 * @param output the underlying socket's OutputStream
56 */
57 TelnetOutputStream(TelnetSocket master, OutputStream output) {
58 this.master = master;
59 this.output = output;
60 }
61
62 // OutputStream interface -------------------------------------------------
63
64 /**
65 * Closes this output stream and releases any system resources associated
66 * with this stream.
67 */
68 @Override
69 public void close() throws IOException {
70 if (output != null) {
71 output.close();
72 output = null;
73 }
74 }
75
76 /**
77 * Flushes this output stream and forces any buffered output bytes to be
78 * written out.
79 */
80 @Override
81 public void flush() throws IOException {
82 if ((master.nvt.binaryMode == false) && (master.nvt.writeCR == true)) {
83 // The last byte sent to this.write() was a CR, which was never
84 // actually sent. So send the CR in ascii mode, then flush.
85 // CR <anything> -> CR NULL
86 output.write(master.C_CR);
87 output.write(master.C_NUL);
88 master.nvt.writeCR = false;
89 }
90 output.flush();
91 }
92
93 /**
94 * Writes b.length bytes from the specified byte array to this output
95 * stream.
96 */
97 @Override
98 public void write(byte[] b) throws IOException {
99 writeImpl(b, 0, b.length);
100 }
101
102 /**
103 * Writes len bytes from the specified byte array starting at offset off
104 * to this output stream.
105 */
106 @Override
107 public void write(byte[] b, int off, int len) throws IOException {
108 writeImpl(b, off, len);
109 }
110
111 /**
112 * Writes the specified byte to this output stream.
113 */
114 @Override
115 public void write(int b) throws IOException {
116 byte [] bytes = new byte[1];
117 bytes[0] = (byte)b;
118 writeImpl(bytes, 0, 1);
119 }
120
121 /**
122 * Writes b.length bytes from the specified byte array to this output
123 * stream. Note package private access.
124 */
125 void rawWrite(byte[] b) throws IOException {
126 output.write(b, 0, b.length);
127 }
128
129 /**
130 * Writes len bytes from the specified byte array starting at offset off
131 * to this output stream.
132 */
133 private void writeImpl(final byte[] b, final int off,
134 final int len) throws IOException {
135
136 byte [] writeBuffer = new byte[Math.max(len, 4)];
137 int writeBufferI = 0;
138
139 for (int i = 0; i < len; i++) {
140 if (writeBufferI >= writeBuffer.length - 4) {
141 // Flush what we have generated so far and reset the buffer,
142 // because the next byte could generate up to 4 output bytes
143 // (CR <something> <IAC> <IAC>).
144 super.write(writeBuffer, 0, writeBufferI);
145 writeBufferI = 0;
146 }
147
148 // Pull the next byte
149 byte ch = b[i + off];
150
151 if (master.nvt.binaryMode == true) {
152
153 if (ch == master.TELNET_IAC) {
154 // IAC -> IAC IAC
155 writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
156 writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
157 } else {
158 // Anything else -> just send
159 writeBuffer[writeBufferI++] = ch;
160 }
161 continue;
162 }
163
164 // Non-binary mode: more complicated. We use writeCR to handle
165 // the case that the last byte of b was a CR.
166
167 // Bare carriage return -> CR NUL
168 if (ch == master.C_CR) {
169 if (master.nvt.writeCR == true) {
170 // Flush the previous CR to the stream.
171 // CR <anything> -> CR NULL
172 writeBuffer[writeBufferI++] = (byte)master.C_CR;
173 writeBuffer[writeBufferI++] = (byte)master.C_NUL;
174 }
175 master.nvt.writeCR = true;
176 } else if (ch == master.C_LF) {
177 if (master.nvt.writeCR == true) {
178 // CR LF -> CR LF
179 writeBuffer[writeBufferI++] = (byte)master.C_CR;
180 writeBuffer[writeBufferI++] = (byte)master.C_LF;
181 master.nvt.writeCR = false;
182 } else {
183 // Bare LF -> LF
184 writeBuffer[writeBufferI++] = ch;
185 }
186 } else if (ch == master.TELNET_IAC) {
187 if (master.nvt.writeCR == true) {
188 // CR <anything> -> CR NULL
189 writeBuffer[writeBufferI++] = (byte)master.C_CR;
190 writeBuffer[writeBufferI++] = (byte)master.C_NUL;
191 master.nvt.writeCR = false;
192 }
193 // IAC -> IAC IAC
194 writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
195 writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
196 } else {
197 if (master.nvt.writeCR == true) {
198 // CR <anything> -> CR NULL
199 writeBuffer[writeBufferI++] = (byte)master.C_CR;
200 writeBuffer[writeBufferI++] = (byte)master.C_NUL;
201 master.nvt.writeCR = false;
202 } else {
203 // Normal character */
204 writeBuffer[writeBufferI++] = ch;
205 }
206 }
207
208 } // while (i < userbuf.length)
209
210 if (writeBufferI > 0) {
211 // Flush what we have generated so far and reset the buffer.
212 super.write(writeBuffer, 0, writeBufferI);
213 }
214 }
215
216
217 }