update e621 (site changed + move to not deprecated BasicSupport
[fanfix.git] / src / jexer / net / TelnetInputStream.java
CommitLineData
daa4106c 1/*
ea91242c
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
ea91242c 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
ea91242c 7 *
e16dda65
KL
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
ea91242c 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
ea91242c 17 *
e16dda65
KL
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
ea91242c
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.net;
30
31import java.io.InputStream;
32import java.io.IOException;
9b1afdde 33import java.util.ArrayList;
ea91242c
KL
34import java.util.Map;
35import java.util.TreeMap;
36
42873e30 37import jexer.backend.SessionInfo;
ea91242c
KL
38import static jexer.net.TelnetSocket.*;
39
40/**
41 * TelnetInputStream works with TelnetSocket to perform the telnet protocol.
42 */
051e2913 43public class TelnetInputStream extends InputStream implements SessionInfo {
ea91242c 44
d36057df
KL
45 // ------------------------------------------------------------------------
46 // Constants --------------------------------------------------------------
47 // ------------------------------------------------------------------------
48
49 // ------------------------------------------------------------------------
50 // Variables --------------------------------------------------------------
51 // ------------------------------------------------------------------------
52
ea91242c
KL
53 /**
54 * The root TelnetSocket that has my telnet protocol state.
55 */
56 private TelnetSocket master;
57
58 /**
59 * The raw socket's InputStream.
60 */
61 private InputStream input;
62
005ec497
KL
63 /**
64 * The telnet-aware OutputStream.
65 */
66 private TelnetOutputStream output;
67
68 /**
69 * Persistent read buffer. In practice this will only be used if the
70 * single-byte read() is called sometime.
71 */
72 private byte [] readBuffer;
73
74 /**
75 * Current writing position in readBuffer - what is passed into
76 * input.read().
77 */
78 private int readBufferEnd;
79
80 /**
81 * Current read position in readBuffer - what is passed to the client in
82 * response to this.read().
83 */
84 private int readBufferStart;
85
d36057df
KL
86 /**
87 * User name.
88 */
89 private String username = "";
90
91 /**
92 * Language.
93 */
94 private String language = "en_US";
95
96 /**
97 * Text window width.
98 */
99 private int windowWidth = 80;
100
101 /**
102 * Text window height.
103 */
104 private int windowHeight = 24;
105
106 /**
107 * When true, the last read byte from the remote side was IAC.
108 */
109 private boolean iac = false;
110
111 /**
112 * When true, we are in the middle of a DO/DONT/WILL/WONT negotiation.
113 */
114 private boolean dowill = false;
115
116 /**
117 * The telnet option being negotiated.
118 */
119 private int dowillType = 0;
120
121 /**
122 * When true, we are waiting to see the end of the sub-negotiation
123 * sequence.
124 */
125 private boolean subnegEnd = false;
126
127 /**
128 * When true, the last byte read from the remote side was CR.
129 */
130 private boolean readCR = false;
131
132 /**
133 * The subnegotiation buffer.
134 */
135 private ArrayList<Byte> subnegBuffer;
136
137 // ------------------------------------------------------------------------
138 // Constructors -----------------------------------------------------------
139 // ------------------------------------------------------------------------
140
ea91242c
KL
141 /**
142 * Package private constructor.
143 *
144 * @param master the master TelnetSocket
145 * @param input the underlying socket's InputStream
005ec497 146 * @param output the telnet-aware OutputStream
ea91242c 147 */
005ec497
KL
148 TelnetInputStream(final TelnetSocket master, final InputStream input,
149 final TelnetOutputStream output) {
150
ea91242c
KL
151 this.master = master;
152 this.input = input;
005ec497
KL
153 this.output = output;
154
155 // Setup new read buffer
156 readBuffer = new byte[1024];
157 readBufferStart = 0;
158 readBufferEnd = 0;
9b1afdde 159 subnegBuffer = new ArrayList<Byte>();
ea91242c
KL
160 }
161
d36057df
KL
162 // ------------------------------------------------------------------------
163 // SessionInfo ------------------------------------------------------------
164 // ------------------------------------------------------------------------
ea91242c
KL
165
166 /**
167 * Username getter.
168 *
169 * @return the username
170 */
171 public String getUsername() {
172 return this.username;
173 }
174
175 /**
176 * Username setter.
177 *
178 * @param username the value
179 */
180 public void setUsername(final String username) {
181 this.username = username;
182 }
183
184 /**
185 * Language getter.
186 *
187 * @return the language
188 */
189 public String getLanguage() {
190 return this.language;
191 }
192
193 /**
194 * Language setter.
195 *
196 * @param language the value
197 */
198 public void setLanguage(final String language) {
199 this.language = language;
200 }
201
1dac6b8d
KL
202 /**
203 * Get the terminal type as reported by the telnet Terminal Type option.
204 *
205 * @return the terminal type
206 */
207 public String getTerminalType() {
208 return master.terminalType;
209 }
210
ea91242c
KL
211 /**
212 * Text window width getter.
213 *
214 * @return the window width
215 */
216 public int getWindowWidth() {
217 return windowWidth;
218 }
219
220 /**
221 * Text window height getter.
222 *
223 * @return the window height
224 */
225 public int getWindowHeight() {
226 return windowHeight;
227 }
228
229 /**
230 * Re-query the text window size.
231 */
232 public void queryWindowSize() {
233 // NOP
234 }
235
d36057df
KL
236 // ------------------------------------------------------------------------
237 // InputStream ------------------------------------------------------------
238 // ------------------------------------------------------------------------
ea91242c
KL
239
240 /**
241 * Returns an estimate of the number of bytes that can be read (or
242 * skipped over) from this input stream without blocking by the next
243 * invocation of a method for this input stream.
244 *
9b1afdde
KL
245 * @return an estimate of the number of bytes that can be read (or
246 * skipped over) from this input stream without blocking or 0 when it
247 * reaches the end of the input stream.
248 * @throws IOException if an I/O error occurs
ea91242c
KL
249 */
250 @Override
251 public int available() throws IOException {
005ec497
KL
252 if (readBuffer == null) {
253 throw new IOException("InputStream is closed");
254 }
255 if (readBufferEnd - readBufferStart > 0) {
256 return (readBufferEnd - readBufferStart);
257 }
258 return input.available();
ea91242c
KL
259 }
260
261 /**
262 * Closes this input stream and releases any system resources associated
263 * with the stream.
9b1afdde
KL
264 *
265 * @throws IOException if an I/O error occurs
ea91242c
KL
266 */
267 @Override
268 public void close() throws IOException {
005ec497
KL
269 if (readBuffer != null) {
270 readBuffer = null;
271 input.close();
272 }
ea91242c
KL
273 }
274
275 /**
276 * Marks the current position in this input stream.
9b1afdde
KL
277 *
278 * @param readLimit the maximum limit of bytes that can be read before
279 * the mark position becomes invalid
ea91242c
KL
280 */
281 @Override
9b1afdde 282 public void mark(final int readLimit) {
005ec497 283 // Do nothing
ea91242c
KL
284 }
285
286 /**
287 * Tests if this input stream supports the mark and reset methods.
9b1afdde
KL
288 *
289 * @return true if this stream instance supports the mark and reset
290 * methods; false otherwise
ea91242c
KL
291 */
292 @Override
293 public boolean markSupported() {
294 return false;
295 }
296
297 /**
298 * Reads the next byte of data from the input stream.
9b1afdde
KL
299 *
300 * @return the next byte of data, or -1 if there is no more data because
301 * the end of the stream has been reached.
302 * @throws IOException if an I/O error occurs
ea91242c
KL
303 */
304 @Override
305 public int read() throws IOException {
005ec497
KL
306
307 // If the post-processed buffer has bytes, use that.
308 if (readBufferEnd - readBufferStart > 0) {
309 readBufferStart++;
310 return readBuffer[readBufferStart - 1];
311 }
312
313 // The buffer is empty, so reset the indexes to 0.
314 readBufferStart = 0;
315 readBufferEnd = 0;
316
317 // Read some fresh data and run it through the telnet protocol.
318 int rc = readImpl(readBuffer, readBufferEnd,
319 readBuffer.length - readBufferEnd);
320
321 // If we got something, return it.
322 if (rc > 0) {
1dac6b8d 323 readBufferEnd += rc;
005ec497
KL
324 readBufferStart++;
325 return readBuffer[readBufferStart - 1];
326 }
327 // If we read 0, I screwed up big time.
328 assert (rc != 0);
329
330 // We read -1 (EOF).
331 return rc;
ea91242c
KL
332 }
333
334 /**
335 * Reads some number of bytes from the input stream and stores them into
336 * the buffer array b.
9b1afdde
KL
337 *
338 * @param b the buffer into which the data is read.
339 * @return the total number of bytes read into the buffer, or -1 if there
340 * is no more data because the end of the stream has been reached.
341 * @throws IOException if an I/O error occurs
ea91242c
KL
342 */
343 @Override
9b1afdde 344 public int read(final byte[] b) throws IOException {
005ec497 345 return read(b, 0, b.length);
ea91242c
KL
346 }
347
348 /**
349 * Reads up to len bytes of data from the input stream into an array of
350 * bytes.
9b1afdde
KL
351 *
352 * @param b the buffer into which the data is read.
353 * @param off the start offset in array b at which the data is written.
354 * @param len the maximum number of bytes to read.
355 * @return the total number of bytes read into the buffer, or -1 if there
356 * is no more data because the end of the stream has been reached.
357 * @throws IOException if an I/O error occurs
ea91242c
KL
358 */
359 @Override
9b1afdde
KL
360 public int read(final byte[] b, final int off,
361 final int len) throws IOException {
362
005ec497
KL
363 // The only time we can return 0 is if len is 0, as per the
364 // InputStream contract.
365 if (len == 0) {
366 return 0;
367 }
368
369 // If the post-processed buffer has bytes, use that.
370 if (readBufferEnd - readBufferStart > 0) {
371 int n = Math.min(len, readBufferEnd - readBufferStart);
372 System.arraycopy(b, off, readBuffer, readBufferStart, n);
373 readBufferStart += n;
374 return n;
375 }
376
377 // The buffer is empty, so reset the indexes to 0.
378 readBufferStart = 0;
379 readBufferEnd = 0;
380
381 // The maximum number of bytes we will ask for will definitely be
382 // within the bounds of what we can return in a single call.
383 int n = Math.min(len, readBuffer.length);
384
385 // Read some fresh data and run it through the telnet protocol.
386 int rc = readImpl(readBuffer, readBufferEnd, n);
387
388 // If we got something, return it.
389 if (rc > 0) {
9b1afdde 390 System.arraycopy(readBuffer, 0, b, off, rc);
005ec497
KL
391 return rc;
392 }
393 // If we read 0, I screwed up big time.
394 assert (rc != 0);
395
396 // We read -1 (EOF).
397 return rc;
ea91242c
KL
398 }
399
400 /**
401 * Repositions this stream to the position at the time the mark method
9b1afdde
KL
402 * was last called on this input stream. This is not supported by
403 * TelnetInputStream, so IOException is always thrown.
404 *
405 * @throws IOException if this function is used
ea91242c
KL
406 */
407 @Override
408 public void reset() throws IOException {
005ec497 409 throw new IOException("InputStream does not support mark/reset");
ea91242c
KL
410 }
411
412 /**
413 * Skips over and discards n bytes of data from this input stream.
9b1afdde
KL
414 *
415 * @param n the number of bytes to be skipped
416 * @return the actual number of bytes skipped
417 * @throws IOException if an I/O error occurs
ea91242c
KL
418 */
419 @Override
9b1afdde 420 public long skip(final long n) throws IOException {
005ec497
KL
421 if (n < 0) {
422 return 0;
423 }
424 for (int i = 0; i < n; i++) {
425 read();
426 }
427 return n;
ea91242c
KL
428 }
429
d36057df
KL
430 // ------------------------------------------------------------------------
431 // TelnetInputStream ------------------------------------------------------
432 // ------------------------------------------------------------------------
ea91242c
KL
433
434 /**
435 * For debugging, return a descriptive string for this telnet option.
436 * These are pulled from: http://www.iana.org/assignments/telnet-options
437 *
9b1afdde 438 * @param option the telnet option byte
ea91242c
KL
439 * @return a string describing the telnet option code
440 */
0d47c546 441 @SuppressWarnings("unused")
ea91242c
KL
442 private String optionString(final int option) {
443 switch (option) {
444 case 0: return "Binary Transmission";
445 case 1: return "Echo";
446 case 2: return "Reconnection";
447 case 3: return "Suppress Go Ahead";
448 case 4: return "Approx Message Size Negotiation";
449 case 5: return "Status";
450 case 6: return "Timing Mark";
451 case 7: return "Remote Controlled Trans and Echo";
452 case 8: return "Output Line Width";
453 case 9: return "Output Page Size";
454 case 10: return "Output Carriage-Return Disposition";
455 case 11: return "Output Horizontal Tab Stops";
456 case 12: return "Output Horizontal Tab Disposition";
457 case 13: return "Output Formfeed Disposition";
458 case 14: return "Output Vertical Tabstops";
459 case 15: return "Output Vertical Tab Disposition";
460 case 16: return "Output Linefeed Disposition";
461 case 17: return "Extended ASCII";
462 case 18: return "Logout";
463 case 19: return "Byte Macro";
464 case 20: return "Data Entry Terminal";
465 case 21: return "SUPDUP";
466 case 22: return "SUPDUP Output";
467 case 23: return "Send Location";
468 case 24: return "Terminal Type";
469 case 25: return "End of Record";
470 case 26: return "TACACS User Identification";
471 case 27: return "Output Marking";
472 case 28: return "Terminal Location Number";
473 case 29: return "Telnet 3270 Regime";
474 case 30: return "X.3 PAD";
475 case 31: return "Negotiate About Window Size";
476 case 32: return "Terminal Speed";
477 case 33: return "Remote Flow Control";
478 case 34: return "Linemode";
479 case 35: return "X Display Location";
480 case 36: return "Environment Option";
481 case 37: return "Authentication Option";
482 case 38: return "Encryption Option";
483 case 39: return "New Environment Option";
484 case 40: return "TN3270E";
485 case 41: return "XAUTH";
486 case 42: return "CHARSET";
487 case 43: return "Telnet Remote Serial Port (RSP)";
488 case 44: return "Com Port Control Option";
489 case 45: return "Telnet Suppress Local Echo";
490 case 46: return "Telnet Start TLS";
491 case 47: return "KERMIT";
492 case 48: return "SEND-URL";
493 case 49: return "FORWARD_X";
494 case 138: return "TELOPT PRAGMA LOGON";
495 case 139: return "TELOPT SSPI LOGON";
496 case 140: return "TELOPT PRAGMA HEARTBEAT";
497 case 255: return "Extended-Options-List";
498 default:
499 if ((option >= 50) && (option <= 137)) {
500 return "Unassigned";
501 }
502 return "UNKNOWN - OTHER";
503 }
504 }
505
506 /**
507 * Send a DO/DON'T/WILL/WON'T response to the remote side.
508 *
509 * @param response a TELNET_DO/DONT/WILL/WONT byte
510 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 511 * @throws IOException if an I/O error occurs
ea91242c
KL
512 */
513 private void respond(final int response,
514 final int option) throws IOException {
515
516 byte [] buffer = new byte[3];
21a7aa8a
KL
517 buffer[0] = (byte) TELNET_IAC;
518 buffer[1] = (byte) response;
519 buffer[2] = (byte) option;
ea91242c 520
005ec497 521 output.rawWrite(buffer);
ea91242c
KL
522 }
523
524 /**
525 * Tell the remote side we WILL support an option.
526 *
527 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 528 * @throws IOException if an I/O error occurs
ea91242c
KL
529 */
530 private void WILL(final int option) throws IOException {
531 respond(TELNET_WILL, option);
532 }
533
534 /**
535 * Tell the remote side we WON'T support an option.
536 *
537 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 538 * @throws IOException if an I/O error occurs
ea91242c
KL
539 */
540 private void WONT(final int option) throws IOException {
541 respond(TELNET_WONT, option);
542 }
543
544 /**
545 * Tell the remote side we DO support an option.
546 *
547 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 548 * @throws IOException if an I/O error occurs
ea91242c
KL
549 */
550 private void DO(final int option) throws IOException {
551 respond(TELNET_DO, option);
552 }
553
554 /**
555 * Tell the remote side we DON'T support an option.
556 *
557 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 558 * @throws IOException if an I/O error occurs
ea91242c
KL
559 */
560 private void DONT(final int option) throws IOException {
561 respond(TELNET_DONT, option);
562 }
563
564 /**
565 * Tell the remote side we WON't or DON'T support an option.
566 *
567 * @param remoteQuery a TELNET_DO/DONT/WILL/WONT byte
568 * @param option telnet option byte (binary mode, term type, etc.)
9b1afdde 569 * @throws IOException if an I/O error occurs
ea91242c
KL
570 */
571 private void refuse(final int remoteQuery,
572 final int option) throws IOException {
573
574 if (remoteQuery == TELNET_DO) {
575 WONT(option);
576 } else {
577 DONT(option);
578 }
579 }
580
581 /**
9b1afdde 582 * Build sub-negotiation packet (RFC 855).
ea91242c
KL
583 *
584 * @param option telnet option
585 * @param response output buffer of response bytes
9b1afdde 586 * @throws IOException if an I/O error occurs
ea91242c
KL
587 */
588 private void telnetSendSubnegResponse(final int option,
589 final byte [] response) throws IOException {
590
591 byte [] buffer = new byte[response.length + 5];
21a7aa8a
KL
592 buffer[0] = (byte) TELNET_IAC;
593 buffer[1] = (byte) TELNET_SB;
594 buffer[2] = (byte) option;
ea91242c 595 System.arraycopy(response, 0, buffer, 3, response.length);
21a7aa8a
KL
596 buffer[response.length + 3] = (byte) TELNET_IAC;
597 buffer[response.length + 4] = (byte) TELNET_SE;
005ec497 598 output.rawWrite(buffer);
ea91242c
KL
599 }
600
601 /**
602 * Telnet option: Terminal Speed (RFC 1079). Client side.
9b1afdde
KL
603 *
604 * @throws IOException if an I/O error occurs
ea91242c
KL
605 */
606 private void telnetSendTerminalSpeed() throws IOException {
607 byte [] response = {0, '3', '8', '4', '0', '0', ',',
608 '3', '8', '4', '0', '0'};
609 telnetSendSubnegResponse(32, response);
610 }
611
612 /**
613 * Telnet option: Terminal Type (RFC 1091). Client side.
9b1afdde
KL
614 *
615 * @throws IOException if an I/O error occurs
ea91242c
KL
616 */
617 private void telnetSendTerminalType() throws IOException {
618 byte [] response = {0, 'v', 't', '1', '0', '0' };
619 telnetSendSubnegResponse(24, response);
620 }
621
622 /**
623 * Telnet option: Terminal Type (RFC 1091). Server side.
9b1afdde
KL
624 *
625 * @throws IOException if an I/O error occurs
ea91242c
KL
626 */
627 private void requestTerminalType() throws IOException {
628 byte [] response = new byte[1];
629 response[0] = 1;
630 telnetSendSubnegResponse(24, response);
631 }
632
633 /**
634 * Telnet option: Terminal Speed (RFC 1079). Server side.
9b1afdde
KL
635 *
636 * @throws IOException if an I/O error occurs
ea91242c
KL
637 */
638 private void requestTerminalSpeed() throws IOException {
639 byte [] response = new byte[1];
640 response[0] = 1;
641 telnetSendSubnegResponse(32, response);
642 }
643
644 /**
645 * Telnet option: New Environment (RFC 1572). Server side.
9b1afdde
KL
646 *
647 * @throws IOException if an I/O error occurs
ea91242c
KL
648 */
649 private void requestEnvironment() throws IOException {
650 byte [] response = new byte[1];
651 response[0] = 1;
652 telnetSendSubnegResponse(39, response);
653 }
654
655 /**
9b1afdde
KL
656 * Send the options we want to negotiate on.
657 *
658 * <p>The options we use are:
659 *
660 * <p>
661 * <pre>
ea91242c
KL
662 * Binary Transmission RFC 856
663 * Suppress Go Ahead RFC 858
664 * Negotiate About Window Size RFC 1073
665 * Terminal Type RFC 1091
666 * Terminal Speed RFC 1079
667 * New Environment RFC 1572
668 *
669 * When run as a server:
9b1afdde
KL
670 * Echo RFC 857
671 * </pre>
672 *
673 * @throws IOException if an I/O error occurs
ea91242c 674 */
005ec497 675 void telnetSendOptions() throws IOException {
9b1afdde 676 if (master.binaryMode == false) {
ea91242c
KL
677 // Binary Transmission: must ask both do and will
678 DO(0);
679 WILL(0);
680 }
681
9b1afdde 682 if (master.goAhead == true) {
ea91242c
KL
683 // Suppress Go Ahead
684 DO(3);
685 WILL(3);
686 }
687
688 // Server only options
9b1afdde 689 if (master.isServer == true) {
ea91242c
KL
690 // Enable Echo - I echo to them, they do not echo back to me.
691 DONT(1);
692 WILL(1);
693
9b1afdde 694 if (master.doTermType == true) {
ea91242c
KL
695 // Terminal type - request it
696 DO(24);
697 }
698
9b1afdde 699 if (master.doTermSpeed == true) {
ea91242c
KL
700 // Terminal speed - request it
701 DO(32);
702 }
703
9b1afdde 704 if (master.doNAWS == true) {
ea91242c
KL
705 // NAWS - request it
706 DO(31);
707 }
708
9b1afdde 709 if (master.doEnvironment == true) {
ea91242c
KL
710 // Environment - request it
711 DO(39);
712 }
713
714 } else {
715
9b1afdde 716 if (master.doTermType == true) {
ea91242c
KL
717 // Terminal type - request it
718 WILL(24);
719 }
720
9b1afdde 721 if (master.doTermSpeed == true) {
ea91242c
KL
722 // Terminal speed - request it
723 WILL(32);
724 }
725
9b1afdde 726 if (master.doNAWS == true) {
ea91242c
KL
727 // NAWS - request it
728 WILL(31);
729 }
730
9b1afdde 731 if (master.doEnvironment == true) {
ea91242c
KL
732 // Environment - request it
733 WILL(39);
734 }
ea91242c 735 }
9b1afdde
KL
736
737 // Push it all out
738 output.flush();
ea91242c
KL
739 }
740
741 /**
742 * New Environment parsing state.
743 */
744 private enum EnvState {
745 INIT,
746 TYPE,
747 NAME,
748 VALUE
749 }
750
751 /**
752 * Handle the New Environment option. Note that this implementation
753 * fails to handle ESC as defined in RFC 1572.
754 */
755 private void handleNewEnvironment() {
09944e92 756 Map<String, String> newEnv = new TreeMap<String, String>();
9b1afdde 757
ea91242c
KL
758 EnvState state = EnvState.INIT;
759 StringBuilder name = new StringBuilder();
760 StringBuilder value = new StringBuilder();
761
09944e92
KL
762 /*
763 System.err.printf("handleNewEnvironment() %d bytes\n",
764 subnegBuffer.size());
765 */
766
767 for (int i = 1; i < subnegBuffer.size(); i++) {
9b1afdde 768 Byte b = subnegBuffer.get(i);
09944e92
KL
769 /*
770 System.err.printf(" b: %c %d 0x%02x\n", (char)b.byteValue(),
771 b, b);
772 */
ea91242c
KL
773
774 switch (state) {
775
776 case INIT:
777 // Looking for "IS"
778 if (b == 0) {
779 state = EnvState.TYPE;
780 } else {
781 // The other side isn't following the rules, see ya.
782 return;
783 }
784 break;
785
786 case TYPE:
787 // Looking for "VAR" or "USERVAR"
788 if (b == 0) {
789 // VAR
790 state = EnvState.NAME;
791 name = new StringBuilder();
792 } else if (b == 3) {
793 // USERVAR
794 state = EnvState.NAME;
795 name = new StringBuilder();
796 } else {
797 // The other side isn't following the rules, see ya
798 return;
799 }
800 break;
801
802 case NAME:
803 // Looking for "VALUE" or a name byte
804 if (b == 1) {
805 // VALUE
806 state = EnvState.VALUE;
807 value = new StringBuilder();
808 } else {
809 // Take it as an environment variable name/key byte
9b1afdde 810 name.append((char)b.byteValue());
ea91242c
KL
811 }
812
813 break;
814
815 case VALUE:
816 // Looking for "VAR", "USERVAR", or a name byte, or the end
817 if (b == 0) {
818 // VAR
819 state = EnvState.NAME;
820 if (value.length() > 0) {
09944e92
KL
821 /*
822 System.err.printf("NAME: '%s' VALUE: '%s'\n",
823 name, value);
824 */
825 newEnv.put(name.toString(), value.toString());
ea91242c
KL
826 }
827 name = new StringBuilder();
828 } else if (b == 3) {
829 // USERVAR
830 state = EnvState.NAME;
831 if (value.length() > 0) {
09944e92
KL
832 /*
833 System.err.printf("NAME: '%s' VALUE: '%s'\n",
834 name, value);
835 */
836 newEnv.put(name.toString(), value.toString());
ea91242c
KL
837 }
838 name = new StringBuilder();
839 } else {
840 // Take it as an environment variable value byte
9b1afdde 841 value.append((char)b.byteValue());
ea91242c
KL
842 }
843 break;
9b1afdde
KL
844
845 default:
846 throw new RuntimeException("Invalid state: " + state);
847
ea91242c
KL
848 }
849 }
850
851 if ((name.length() > 0) && (value.length() > 0)) {
09944e92
KL
852 /*
853 System.err.printf("NAME: '%s' VALUE: '%s'\n", name, value);
854 */
855 newEnv.put(name.toString(), value.toString());
ea91242c
KL
856 }
857
09944e92 858 for (String key: newEnv.keySet()) {
ea91242c 859 if (key.equals("LANG")) {
09944e92 860 language = newEnv.get(key);
ea91242c
KL
861 }
862 if (key.equals("LOGNAME")) {
09944e92 863 username = newEnv.get(key);
ea91242c
KL
864 }
865 if (key.equals("USER")) {
09944e92 866 username = newEnv.get(key);
ea91242c
KL
867 }
868 }
869 }
870
871 /**
872 * Handle an option sub-negotiation.
9b1afdde
KL
873 *
874 * @throws IOException if an I/O error occurs
ea91242c
KL
875 */
876 private void handleSubneg() throws IOException {
9b1afdde 877 Byte option;
ea91242c
KL
878
879 // Sanity check: there must be at least 1 byte in subnegBuffer
9b1afdde 880 if (subnegBuffer.size() < 1) {
ea91242c
KL
881 // Buffer too small: the other side is a broken telnetd, it did
882 // not send the right sub-negotiation data. Bail out now.
883 return;
884 }
9b1afdde 885 option = subnegBuffer.get(0);
ea91242c
KL
886
887 switch (option) {
888
889 case 24:
890 // Terminal Type
9b1afdde 891 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 1)) {
ea91242c
KL
892 // Server sent "SEND", we say "IS"
893 telnetSendTerminalType();
894 }
9b1afdde 895 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 0)) {
ea91242c
KL
896 // Client sent "IS", record it
897 StringBuilder terminalString = new StringBuilder();
9b1afdde
KL
898 for (int i = 2; i < subnegBuffer.size(); i++) {
899 terminalString.append((char)subnegBuffer.
900 get(i).byteValue());
ea91242c 901 }
9b1afdde 902 master.terminalType = terminalString.toString();
09944e92
KL
903 /*
904 System.err.printf("terminal type: '%s'\n",
905 master.terminalType);
906 */
ea91242c
KL
907 }
908 break;
909
910 case 32:
911 // Terminal Speed
9b1afdde 912 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 1)) {
ea91242c
KL
913 // Server sent "SEND", we say "IS"
914 telnetSendTerminalSpeed();
915 }
9b1afdde 916 if ((subnegBuffer.size() > 1) && (subnegBuffer.get(1) == 0)) {
ea91242c
KL
917 // Client sent "IS", record it
918 StringBuilder speedString = new StringBuilder();
9b1afdde
KL
919 for (int i = 2; i < subnegBuffer.size(); i++) {
920 speedString.append((char)subnegBuffer.get(i).byteValue());
ea91242c 921 }
9b1afdde 922 master.terminalSpeed = speedString.toString();
09944e92
KL
923 /*
924 System.err.printf("terminal speed: '%s'\n",
925 master.terminalSpeed);
926 */
ea91242c
KL
927 }
928 break;
929
930 case 31:
931 // NAWS
9b1afdde 932 if (subnegBuffer.size() >= 5) {
ea91242c
KL
933 int i = 0;
934
935 i++;
21a7aa8a 936 if (subnegBuffer.get(i) == (byte) TELNET_IAC) {
ea91242c
KL
937 i++;
938 }
0895a25f
KL
939 int width = subnegBuffer.get(i);
940 if (width < 0) {
941 width += 256;
942 }
943 windowWidth = width * 256;
ea91242c
KL
944
945 i++;
21a7aa8a 946 if (subnegBuffer.get(i) == (byte) TELNET_IAC) {
ea91242c
KL
947 i++;
948 }
0895a25f
KL
949 width = subnegBuffer.get(i);
950 windowWidth += width;
951 if (width < 0) {
952 windowWidth += 256;
953 }
ea91242c
KL
954
955 i++;
21a7aa8a 956 if (subnegBuffer.get(i) == (byte) TELNET_IAC) {
ea91242c
KL
957 i++;
958 }
0895a25f
KL
959 int height = subnegBuffer.get(i);
960 if (height < 0) {
961 height += 256;
962 }
963 windowHeight = height * 256;
ea91242c
KL
964
965 i++;
21a7aa8a 966 if (subnegBuffer.get(i) == (byte) TELNET_IAC) {
ea91242c
KL
967 i++;
968 }
0895a25f
KL
969 height = subnegBuffer.get(i);
970 windowHeight += height;
971 if (height < 0) {
972 windowHeight += 256;
973 }
ea91242c
KL
974 }
975 break;
976
977 case 39:
978 // Environment
979 handleNewEnvironment();
980 break;
981
982 default:
983 // Ignore this one
984 break;
985 }
986 }
987
005ec497
KL
988 /**
989 * Reads up to len bytes of data from the input stream into an array of
990 * bytes.
9b1afdde
KL
991 *
992 * @param buf the buffer into which the data is read.
993 * @param off the start offset in array b at which the data is written.
994 * @param len the maximum number of bytes to read.
995 * @return the total number of bytes read into the buffer, or -1 if there
996 * is no more data because the end of the stream has been reached.
997 * @throws IOException if an I/O error occurs
005ec497 998 */
9b1afdde
KL
999 private int readImpl(final byte[] buf, final int off,
1000 final int len) throws IOException {
1001
005ec497 1002 assert (len > 0);
9b1afdde
KL
1003
1004 // The current writing position in buf.
21a7aa8a 1005 int bufN = off;
9b1afdde
KL
1006
1007 // We will keep trying to read() until we have something to return.
1008 do {
1009
21a7aa8a
KL
1010 byte [] buffer = null;
1011 if (master.binaryMode) {
1012 // Binary mode: read up to len bytes. There will never be
1013 // more bytes to pass upstream than there are bytes on the
1014 // wire.
1015 buffer = new byte[len];
1016 } else {
1017 // ASCII mode: read up to len - 2 bytes. There may have been
1018 // some combination of IAC, CR, and NUL from a previous
1019 // readImpl() that could result in more bytes to pass up than
1020 // are on the wire.
1021 buffer = new byte[len - 2];
1022 }
1023
9b1afdde
KL
1024 int bufferN = 0;
1025
1026 // Read some data from the other end
1027 int rc = input.read(buffer);
1028
1029 // Check for EOF or error
1030 if (rc > 0) {
1031 // More data came in
1032 bufferN = rc;
1033 } else {
1034 // EOF, just return it.
1035 return rc;
1036 }
1037
1038 // Loop through the read bytes
1039 for (int i = 0; i < bufferN; i++) {
1040 byte b = buffer[i];
1041
1042 if (subnegEnd == true) {
1043 // Looking for IAC SE to end this subnegotiation
21a7aa8a 1044 if (b == (byte) TELNET_SE) {
9b1afdde
KL
1045 if (iac == true) {
1046 iac = false;
1047 subnegEnd = false;
1048 handleSubneg();
1049 }
21a7aa8a 1050 } else if (b == (byte) TELNET_IAC) {
9b1afdde
KL
1051 if (iac == true) {
1052 // An argument to the subnegotiation option
21a7aa8a 1053 subnegBuffer.add((byte) TELNET_IAC);
9b1afdde
KL
1054 } else {
1055 iac = true;
1056 }
1057 } else {
1058 // An argument to the subnegotiation option
1059 subnegBuffer.add(b);
1060 }
1061 continue;
1062 }
1063
1064 // Look for DO/DON'T/WILL/WON'T option
1065 if (dowill == true) {
1066
1067 // Look for option/
1068 switch (b) {
1069
1070 case 0:
1071 // Binary Transmission
21a7aa8a 1072 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1073 // Server will use binary transmission, yay.
1074 master.binaryMode = true;
21a7aa8a 1075 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1076 // Server asks for binary transmission.
1077 WILL(b);
1078 master.binaryMode = true;
21a7aa8a 1079 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1080 // We're screwed, server won't do binary
1081 // transmission.
1082 master.binaryMode = false;
1083 } else {
1084 // Server demands NVT ASCII mode.
1085 master.binaryMode = false;
1086 }
1087 break;
1088
1089 case 1:
1090 // Echo
21a7aa8a 1091 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1092 // Server will use echo, yay.
1093 master.echoMode = true;
21a7aa8a 1094 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1095 // Server asks for echo.
1096 WILL(b);
1097 master.echoMode = true;
21a7aa8a 1098 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1099 // We're screwed, server won't do echo.
1100 master.echoMode = false;
1101 } else {
1102 // Server demands no echo.
1103 master.echoMode = false;
1104 }
1105 break;
1106
1107 case 3:
1108 // Suppress Go Ahead
21a7aa8a 1109 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1110 // Server will use suppress go-ahead, yay.
1111 master.goAhead = false;
21a7aa8a 1112 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1113 // Server asks for suppress go-ahead.
1114 WILL(b);
1115 master.goAhead = false;
21a7aa8a 1116 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1117 // We're screwed, server won't do suppress
1118 // go-ahead.
1119 master.goAhead = true;
1120 } else {
1121 // Server demands Go-Ahead mode.
1122 master.goAhead = true;
1123 }
1124 break;
1125
1126 case 24:
1127 // Terminal Type - send what's in TERM
21a7aa8a 1128 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1129 // Server will use terminal type, yay.
1130 if (master.isServer
1131 && master.doTermType
1132 ) {
1133 requestTerminalType();
1134 master.doTermType = false;
1135 } else if (!master.isServer) {
1136 master.doTermType = true;
1137 }
21a7aa8a 1138 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1139 // Server asks for terminal type.
1140 WILL(b);
1141 master.doTermType = true;
21a7aa8a 1142 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1143 // We're screwed, server won't do terminal type.
1144 master.doTermType = false;
1145 } else {
1146 // Server will not listen to terminal type.
1147 master.doTermType = false;
1148 }
1149 break;
1150
1151 case 31:
1152 // NAWS
21a7aa8a 1153 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1154 // Server will use NAWS, yay.
1155 master.doNAWS = true;
1156 // NAWS cannot be requested by the server, it is
1157 // only sent by the client.
21a7aa8a 1158 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1159 // Server asks for NAWS.
1160 WILL(b);
1161 master.doNAWS = true;
21a7aa8a 1162 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1163 // Server won't do NAWS.
1164 master.doNAWS = false;
1165 } else {
1166 // Server will not listen to NAWS.
1167 master.doNAWS = false;
1168 }
1169 break;
1170
1171 case 32:
1172 // Terminal Speed
21a7aa8a 1173 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1174 // Server will use terminal speed, yay.
1175 if (master.isServer
1176 && master.doTermSpeed
1177 ) {
1178 requestTerminalSpeed();
1179 master.doTermSpeed = false;
1180 } else if (!master.isServer) {
1181 master.doTermSpeed = true;
1182 }
21a7aa8a 1183 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1184 // Server asks for terminal speed.
1185 WILL(b);
1186 master.doTermSpeed = true;
21a7aa8a 1187 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1188 // We're screwed, server won't do terminal speed.
1189 master.doTermSpeed = false;
1190 } else {
1191 // Server will not listen to terminal speed.
1192 master.doTermSpeed = false;
1193 }
1194 break;
1195
1196 case 39:
1197 // New Environment
21a7aa8a 1198 if (dowillType == (byte) TELNET_WILL) {
9b1afdde
KL
1199 // Server will use NewEnvironment, yay.
1200 if (master.isServer
1201 && master.doEnvironment
1202 ) {
1203 requestEnvironment();
1204 master.doEnvironment = false;
1205 } else if (!master.isServer) {
1206 master.doEnvironment = true;
1207 }
21a7aa8a 1208 } else if (dowillType == (byte) TELNET_DO) {
9b1afdde
KL
1209 // Server asks for NewEnvironment.
1210 WILL(b);
1211 master.doEnvironment = true;
21a7aa8a 1212 } else if (dowillType == (byte) TELNET_WONT) {
9b1afdde
KL
1213 // Server won't do NewEnvironment.
1214 master.doEnvironment = false;
1215 } else {
1216 // Server will not listen to New Environment.
1217 master.doEnvironment = false;
1218 }
1219 break;
1220
1221
1222 default:
1223 // Other side asked for something we don't
1224 // understand. Tell them we will not do this option.
1225 refuse(dowillType, b);
1226 break;
1227 }
1228
1229 dowill = false;
1230 continue;
1231 } // if (dowill == true)
1232
1233 // Perform read processing
21a7aa8a 1234 if (b == (byte) TELNET_IAC) {
9b1afdde
KL
1235
1236 // Telnet command
1237 if (iac == true) {
1238 // IAC IAC -> IAC
21a7aa8a 1239 buf[bufN++] = (byte) TELNET_IAC;
9b1afdde
KL
1240 iac = false;
1241 } else {
1242 iac = true;
1243 }
1244 continue;
1245 } else {
1246 if (iac == true) {
1247
1248 switch (b) {
1249
21a7aa8a
KL
1250 case (byte) TELNET_SE:
1251 // END Sub-Negotiation
9b1afdde 1252 break;
21a7aa8a
KL
1253 case (byte) TELNET_NOP:
1254 // NOP
9b1afdde 1255 break;
21a7aa8a
KL
1256 case (byte) TELNET_DM:
1257 // Data Mark
9b1afdde 1258 break;
21a7aa8a
KL
1259 case (byte) TELNET_BRK:
1260 // Break
9b1afdde 1261 break;
21a7aa8a
KL
1262 case (byte) TELNET_IP:
1263 // Interrupt Process
9b1afdde 1264 break;
21a7aa8a
KL
1265 case (byte) TELNET_AO:
1266 // Abort Output
9b1afdde 1267 break;
21a7aa8a
KL
1268 case (byte) TELNET_AYT:
1269 // Are You There?
9b1afdde 1270 break;
21a7aa8a
KL
1271 case (byte) TELNET_EC:
1272 // Erase Character
9b1afdde 1273 break;
21a7aa8a
KL
1274 case (byte) TELNET_EL:
1275 // Erase Line
9b1afdde 1276 break;
21a7aa8a
KL
1277 case (byte) TELNET_GA:
1278 // Go Ahead
9b1afdde 1279 break;
21a7aa8a
KL
1280 case (byte) TELNET_SB:
1281 // START Sub-Negotiation
9b1afdde
KL
1282 // From here we wait for the IAC SE
1283 subnegEnd = true;
1284 subnegBuffer.clear();
1285 break;
21a7aa8a
KL
1286 case (byte) TELNET_WILL:
1287 // WILL
9b1afdde
KL
1288 dowill = true;
1289 dowillType = b;
1290 break;
21a7aa8a
KL
1291 case (byte) TELNET_WONT:
1292 // WON'T
9b1afdde
KL
1293 dowill = true;
1294 dowillType = b;
1295 break;
21a7aa8a
KL
1296 case (byte) TELNET_DO:
1297 // DO
9b1afdde
KL
1298 dowill = true;
1299 dowillType = b;
9b1afdde 1300 break;
21a7aa8a
KL
1301 case (byte) TELNET_DONT:
1302 // DON'T
9b1afdde
KL
1303 dowill = true;
1304 dowillType = b;
1305 break;
1306 default:
1307 // This should be equivalent to IAC NOP
9b1afdde
KL
1308 break;
1309 }
1310 iac = false;
1311 continue;
1312
1313 } // if (iac == true)
1314
1315 /*
1316 * All of the regular IAC processing is completed at this
1317 * point. Now we need to handle the CR and CR LF cases.
1318 *
1319 * According to RFC 854, in NVT ASCII mode:
1320 * Bare CR -> CR NUL
1321 * CR LF -> CR LF
1322 *
1323 */
1324 if (master.binaryMode == false) {
1325
1326 if (b == C_LF) {
1327 if (readCR == true) {
1328 // This is CR LF. Send CR LF and turn the cr
1329 // flag off.
1330 buf[bufN++] = C_CR;
1331 buf[bufN++] = C_LF;
1332 readCR = false;
1333 continue;
1334 }
1335 // This is bare LF. Send LF.
1336 buf[bufN++] = C_LF;
1337 continue;
1338 }
1339
1340 if (b == C_NUL) {
1341 if (readCR == true) {
1342 // This is CR NUL. Send CR and turn the cr
1343 // flag off.
1344 buf[bufN++] = C_CR;
1345 readCR = false;
1346 continue;
1347 }
1348 // This is bare NUL. Send NUL.
1349 buf[bufN++] = C_NUL;
1350 continue;
1351 }
1352
1353 if (b == C_CR) {
1354 if (readCR == true) {
1355 // This is CR CR. Send a CR NUL and leave
1356 // the cr flag on.
1357 buf[bufN++] = C_CR;
1358 buf[bufN++] = C_NUL;
1359 continue;
1360 }
1361 // This is the first CR. Set the cr flag.
1362 readCR = true;
1363 continue;
1364 }
1365
1366 if (readCR == true) {
1367 // This was a bare CR in the stream.
1368 buf[bufN++] = C_CR;
1369 readCR = false;
1370 }
1371
1372 // This is a regular character. Pass it on.
1373 buf[bufN++] = b;
1374 continue;
1375 }
1376
1377 /*
1378 * This is the case for any of:
1379 *
1380 * 1) A NVT ASCII character that isn't CR, LF, or
1381 * NUL.
1382 *
1383 * 2) A NVT binary character.
1384 *
1385 * For all of these cases, we just pass the character on.
1386 */
1387 buf[bufN++] = b;
1388
1389 } // if (b == TELNET_IAC)
1390
1391 } // for (int i = 0; i < bufferN; i++)
1392
1393 } while (bufN == 0);
1394
1395 // Return bytes read
1396 return bufN;
005ec497
KL
1397 }
1398
ea91242c 1399}