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