1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 /**
27 * SSHTools - Java SSH API The contents of this package has been derived from
28 * the TelnetD library available from http://sourceforge.net/projects/telnetd
29 * The original license of the source code is as follows: TelnetD library
30 * (embeddable telnet daemon) Copyright (C) 2000 Dieter Wimberger This library
31 * is free software; you can either redistribute it and/or modify it under the
32 * terms of the GNU Lesser General Public License version 2.1,1999 as
33 * published by the Free Software Foundation (see copy received along with the
34 * library), or under the terms of the BSD-style license received along with
35 * this library.
36 */
37 package com.sshtools.daemon.terminal;
38
39 import com.sshtools.j2ssh.io;
40 import com.sshtools.j2ssh.session;
41
42 import java.io;
43
44
45 /**
46 *
47 *
48 * @author $author$
49 * @version $Revision: 1.13 $
50 */
51 public class TerminalIO implements PseudoTerminal {
52 // implements BasicTerminalIO {
53
54 /** */
55 public static final int EOL_CRLF = 1;
56
57 /** */
58 public static final int EOL_CR = 2;
59
60 /** */
61 public static final int[] HOME = { 0, 0 };
62
63 /** */
64 public static final int IOERROR = -1; //CTRL-D beim login
65
66 /** */
67 public static final int
68 // Positioning 10xx
69 UP = 1001; //CTRL-D beim login
70
71 /** */
72 public static final int DOWN = 1002; //CTRL-D beim login
73
74 /** */
75 public static final int RIGHT = 1003; //CTRL-D beim login
76
77 /** */
78 public static final int LEFT = 1004; //CTRL-D beim login
79
80 /** */
81 public static final int STORECURSOR = 1051; //CTRL-D beim login
82
83 /** */
84 public static final int RESTORECURSOR = 1052; //CTRL-D beim login
85
86 /** */
87 public static final int
88 // Erasing 11xx
89 EEOL = 1100; //CTRL-D beim login
90
91 /** */
92 public static final int EBOL = 1101; //CTRL-D beim login
93
94 /** */
95 public static final int EEL = 1103; //CTRL-D beim login
96
97 /** */
98 public static final int EEOS = 1104; //CTRL-D beim login
99
100 /** */
101 public static final int EBOS = 1105; //CTRL-D beim login
102
103 /** */
104 public static final int EES = 1106; //CTRL-D beim login
105
106 /** */
107 public static final int
108 // Escape Sequence-ing 12xx
109 ESCAPE = 1200; //CTRL-D beim login
110
111 /** */
112 public static final int BYTEMISSING = 1201; //CTRL-D beim login
113
114 /** */
115 public static final int UNRECOGNIZED = 1202; //CTRL-D beim login
116
117 /** */
118 public static final int
119 // Control Characters 13xx
120 ENTER = 10; //CTRL-D beim login
121
122 /** */
123 public static final int
124 //ENTER = 1300, //LF is ENTER at the moment
125 TABULATOR = 1301; //CTRL-D beim login
126
127 /** */
128 public static final int DELETE = 1302; //CTRL-D beim login
129
130 /** */
131 public static final int BACKSPACE = 1303; //CTRL-D beim login
132
133 /** */
134 public static final int COLORINIT = 1304; //CTRL-D beim login
135
136 /** */
137 public static final int HANDLED = 1305; //CTRL-D beim login
138
139 /** */
140 public static final int LOGOUTREQUEST = 1306; //CTRL-D beim login
141
142 /** */
143 public static final int LineUpdate = 475;
144
145 /** */
146 public static final int CharacterUpdate = 476;
147
148 /** */
149 public static final int ScreenpartUpdate = 477;
150
151 /** */
152 public static final int EditBuffer = 575;
153
154 /** */
155 public static final int LineEditBuffer = 576;
156
157 /** */
158 public static final int BEL = 7;
159
160 /** */
161 public static final int BS = 8;
162
163 /** */
164 public static final int DEL = 127;
165
166 /** */
167 public static final int CR = 13;
168
169 /** */
170 public static final int LF = 10;
171
172 /** */
173 public static final int FCOLOR = 10001;
174
175 /** */
176 public static final int BCOLOR = 10002;
177
178 /** */
179 public static final int STYLE = 10003;
180
181 /** */
182 public static final int RESET = 10004;
183
184 /** */
185 public static final int BOLD = 1;
186
187 /** */
188 public static final int BOLD_OFF = 22;
189
190 /** */
191 public static final int ITALIC = 3;
192
193 /** */
194 public static final int ITALIC_OFF = 23;
195
196 /** */
197 public static final int BLINK = 5;
198
199 /** */
200 public static final int BLINK_OFF = 25;
201
202 /** */
203 public static final int UNDERLINED = 4;
204
205 /** */
206 public static final int UNDERLINED_OFF = 24;
207
208 //Constants
209
210 /** */
211 public static final int BLACK = 30;
212
213 /** */
214 public static final int RED = 31;
215
216 /** */
217 public static final int GREEN = 32;
218
219 /** */
220 public static final int YELLOW = 33;
221
222 /** */
223 public static final int BLUE = 34;
224
225 /** */
226 public static final int MAGENTA = 35;
227
228 /** */
229 public static final int CYAN = 36;
230
231 /** */
232 public static final int white = 37;
233
234 /** */
235 public static final String CRLF = "\r\n";
236 private Terminal terminal;
237 private DataInputStream in;
238 private DataOutputStream out;
239 private boolean closing;
240 private boolean cr;
241 private boolean nl;
242 private boolean acousticSignalling; //flag for accoustic signalling
243 private boolean autoflush; //flag for autoflushing mode
244 private int eol = EOL_CRLF;
245 private int lastByte;
246 private boolean uselast = false;
247 private Colorizer color = Colorizer.getReference();
248 private String term;
249 private int cols;
250 private int rows;
251 private PipedInputStream masterIn;
252 private PipedOutputStream masterOut;
253 private InputStream slaveIn;
254 private OutputStream slaveOut;
255 private IOStreamConnector ios;
256
257 //private OutputStream masterOut;
258 // private OutputStream slaveOut = new SlaveOutputStream();
259 public TerminalIO(InputStream in, OutputStream out, String term, int cols,
260 int rows) throws IOException {
261 attachStreams(in, out);
262 this.term = term;
263 this.rows = rows;
264 this.cols = cols;
265 acousticSignalling = true;
266 masterOut = new PipedOutputStream();
267 masterIn = new PipedInputStream(masterOut);
268 autoflush = true;
269 closing = false;
270 cr = false;
271
272 //set default terminal
273 setDefaultTerminal();
274 }
275
276 /**
277 *
278 *
279 * @return
280 */
281 public InputStream getMasterInputStream() {
282 return masterIn;
283 }
284
285 /**
286 *
287 *
288 * @param slaveIn
289 */
290 public void bindSlaveInputStream(InputStream slaveIn) {
291 this.slaveIn = slaveIn;
292 this.ios = new IOStreamConnector(slaveIn, masterOut);
293 }
294
295 /**
296 *
297 *
298 * @param slaveOut
299 */
300 public void bindSlaveOutputStream(OutputStream slaveOut) {
301 this.slaveOut = slaveOut;
302 }
303
304 /**
305 *
306 *
307 * @return
308 */
309 public OutputStream getSlaveOutputStream() {
310 return slaveOut;
311 }
312
313 /**
314 *
315 *
316 * @return
317 */
318 public int getWidth() {
319 return 0;
320 }
321
322 /**
323 *
324 *
325 * @return
326 */
327 public int getHeight() {
328 return 0;
329 }
330
331 /**
332 *
333 *
334 * @return
335 */
336 public String getTerm() {
337 return terminal.getName();
338 }
339
340 /**
341 *
342 *
343 * @return
344 */
345 public String getEncodedTerminalModes() {
346 return "";
347 }
348
349 /* public void setMasterOutputStream(OutputStream masterOut) {
350 this.masterOut = masterOut;
351 }*/
352 public InputStream getAttachedInputStream() throws IOException {
353 if (in == null) {
354 throw new IOException(
355 "The teminal is not attached to an InputStream");
356 }
357
358 return in;
359 }
360
361 /**
362 *
363 *
364 * @return
365 *
366 * @throws IOException
367 */
368 public OutputStream getAttachedOutputStream() throws IOException {
369 if (out == null) {
370 throw new IOException(
371 "The terminal is not attached to an OutputStream");
372 }
373
374 return out;
375 }
376
377 /**
378 *
379 */
380 public void detachStreams() {
381 this.in = null;
382 this.out = null;
383 }
384
385 /**
386 *
387 *
388 * @return
389 */
390 public int getEOL() {
391 return eol;
392 }
393
394 /**
395 *
396 *
397 * @return
398 */
399 public String getEOLString() {
400 return ((eol == EOL_CR) ? "\r" : "\r\n");
401 }
402
403 /**
404 *
405 *
406 * @param eol
407 */
408 public void setEOL(int eol) {
409 this.eol = eol;
410 }
411
412 /**
413 *
414 *
415 * @param in
416 * @param out
417 */
418 public void attachStreams(InputStream in, OutputStream out) {
419 this.in = new DataInputStream(new BufferedInputStream(in));
420 this.out = new DataOutputStream(new BufferedOutputStream(out));
421 }
422
423 /**
424 *
425 *
426 * @return
427 *
428 * @throws IOException
429 */
430 public int read() throws IOException {
431 int i = stripCRSeq(rawread());
432
433 //translate possible control sequences
434 i = terminal.translateControlCharacter(i);
435
436 if ((i > 256) && (i == ESCAPE)) {
437 i = handleEscapeSequence(i);
438 }
439
440 return i;
441 }
442
443 /**
444 *
445 *
446 * @param ch
447 *
448 * @throws IOException
449 */
450 public void write(char ch) throws IOException {
451 write((byte) ch);
452
453 if (autoflush) {
454 flush();
455 }
456 }
457
458 /**
459 *
460 *
461 * @param str
462 *
463 * @throws IOException
464 */
465 public void write(String str) throws IOException {
466 write((color.colorize(str, terminal.supportsSGR())).getBytes());
467
468 if (autoflush) {
469 flush();
470 }
471 }
472
473 /**
474 *
475 *
476 * @param str
477 *
478 * @throws IOException
479 */
480 public void println(String str) throws IOException {
481 write(str);
482 write(getEOLString().getBytes());
483
484 if (autoflush) {
485 flush();
486 }
487 }
488
489 /**
490 *
491 *
492 * @throws IOException
493 */
494 public void println() throws IOException {
495 write(getEOLString().getBytes());
496
497 if (autoflush) {
498 flush();
499 }
500 }
501
502 /**
503 *
504 *
505 * @throws IOException
506 */
507 public void eraseToEndOfLine() throws IOException {
508 doErase(EEOL);
509 }
510
511 /**
512 *
513 *
514 * @throws IOException
515 */
516 public void eraseToBeginOfLine() throws IOException {
517 doErase(EBOL);
518 }
519
520 /**
521 *
522 *
523 * @throws IOException
524 */
525 public void eraseLine() throws IOException {
526 doErase(EEL);
527 }
528
529 /**
530 *
531 *
532 * @throws IOException
533 */
534 public void eraseToEndOfScreen() throws IOException {
535 doErase(EEOS);
536 }
537
538 /**
539 *
540 *
541 * @throws IOException
542 */
543 public void eraseToBeginOfScreen() throws IOException {
544 doErase(EBOS);
545 }
546
547 /**
548 *
549 *
550 * @throws IOException
551 */
552 public void eraseScreen() throws IOException {
553 doErase(EES);
554 }
555
556 private void doErase(int funcConst) throws IOException {
557 write(terminal.getEraseSequence(funcConst));
558
559 if (autoflush) {
560 flush();
561 }
562 }
563
564 /**
565 *
566 *
567 * @param direction
568 * @param times
569 *
570 * @throws IOException
571 */
572 public void moveCursor(int direction, int times) throws IOException {
573 write(terminal.getCursorMoveSequence(direction, times));
574
575 if (autoflush) {
576 flush();
577 }
578 }
579
580 /**
581 *
582 *
583 * @param times
584 *
585 * @throws IOException
586 */
587 public void moveLeft(int times) throws IOException {
588 moveCursor(LEFT, times);
589 }
590
591 /**
592 *
593 *
594 * @param times
595 *
596 * @throws IOException
597 */
598 public void moveRight(int times) throws IOException {
599 moveCursor(RIGHT, times);
600 }
601
602 /**
603 *
604 *
605 * @param times
606 *
607 * @throws IOException
608 */
609 public void moveUp(int times) throws IOException {
610 moveCursor(UP, times);
611 }
612
613 /**
614 *
615 *
616 * @param times
617 *
618 * @throws IOException
619 */
620 public void moveDown(int times) throws IOException {
621 moveCursor(DOWN, times);
622 }
623
624 /**
625 *
626 *
627 * @param row
628 * @param col
629 *
630 * @throws IOException
631 */
632 public void setCursor(int row, int col) throws IOException {
633 int[] pos = new int[2];
634 pos[0] = row;
635 pos[1] = col;
636 write(terminal.getCursorPositioningSequence(pos));
637
638 if (autoflush) {
639 flush();
640 }
641 }
642
643 /**
644 *
645 *
646 * @throws IOException
647 */
648 public void homeCursor() throws IOException {
649 write(terminal.getCursorPositioningSequence(HOME));
650
651 if (autoflush) {
652 flush();
653 }
654 }
655
656 /**
657 *
658 *
659 * @throws IOException
660 */
661 public void storeCursor() throws IOException {
662 write(terminal.getSpecialSequence(STORECURSOR));
663 }
664
665 /**
666 *
667 *
668 * @throws IOException
669 */
670 public void restoreCursor() throws IOException {
671 write(terminal.getSpecialSequence(RESTORECURSOR));
672 }
673
674 /**
675 *
676 *
677 * @throws IOException
678 */
679 public void closeInput() throws IOException {
680 if (in == null) {
681 throw new IOException(
682 "The terminal is not attached to an inputstream");
683 }
684
685 in.close();
686 }
687
688 private int read16int() throws IOException {
689 if (in == null) {
690 throw new IOException(
691 "The terminal is not attached to an inputstream");
692 }
693
694 return in.readUnsignedShort();
695 }
696
697 private int rawread() throws IOException {
698 if (in == null) {
699 throw new IOException(
700 "The terminal is not attached to an inputstream");
701 }
702
703 return in.readUnsignedByte();
704 }
705
706 private int stripCRSeq(int input) throws IOException {
707 if (in == null) {
708 throw new IOException(
709 "The terminal is not attached to an inputstream");
710 }
711
712 // Check for CR
713 if (input == CR) {
714 // Mark the current position and test for LF
715 in.mark(1);
716
717 // If there is more data to read, check for LF
718 if (in.available() > 0) {
719 int next = in.readUnsignedByte();
720
721 // If we don't have LF then reset back to the mark position
722 if (next != LF) {
723 in.reset();
724 }
725 }
726
727 return TerminalIO.ENTER;
728 }
729
730 return input;
731 }
732
733 /**
734 *
735 *
736 * @param b
737 *
738 * @throws IOException
739 */
740 public void write(byte b) throws IOException {
741 if (out == null) {
742 throw new IOException(
743 "The terminal is not attached to an outputstream");
744 }
745
746 if (eol == EOL_CRLF) {
747 if (!cr && (b == 10)) {
748 out.write(13);
749
750 //if(masterOut!=null)
751 // masterOut.write(13);
752 }
753
754 //ensure CRLF(\r\n) is written for CR(\r) to adhere
755 //to the telnet protocol.
756 if (cr && (b != 10)) {
757 out.write(10);
758
759 // if(masterOut!=null)
760 // masterOut.write(10);
761 }
762
763 out.write(b);
764
765 // if(masterOut!=null)
766 // masterOut.write(b);
767 if (b == 13) {
768 cr = true;
769 } else {
770 cr = false;
771 }
772 } else {
773 out.write(b);
774
775 // if(masterOut!=null)
776 // masterOut.write(b);
777 }
778 }
779
780 /**
781 *
782 *
783 * @param i
784 *
785 * @throws IOException
786 */
787 public void write(int i) throws IOException {
788 write((byte) i);
789 }
790
791 /**
792 *
793 *
794 * @param sequence
795 *
796 * @throws IOException
797 */
798 public void write(byte[] sequence) throws IOException {
799 for (int z = 0; z < sequence.length; z++) {
800 write(sequence[z]);
801 }
802 }
803
804 /**
805 *
806 *
807 * @param sequence
808 *
809 * @throws IOException
810 */
811 public void write(int[] sequence) throws IOException {
812 for (int j = 0; j < sequence.length; j++) {
813 write((byte) sequence[j]);
814 }
815 }
816
817 /**
818 *
819 *
820 * @throws IOException
821 */
822 public void flush() throws IOException {
823 if (out == null) {
824 throw new IOException(
825 "The terminal is not attached to an outputstream");
826 }
827
828 // If were attached then flush, else ignore
829 out.flush();
830 }
831
832 /**
833 *
834 *
835 * @throws IOException
836 */
837 public void closeOutput() throws IOException {
838 if (out == null) {
839 throw new IOException(
840 "The terminal is not attached to an outputstream");
841 }
842
843 closing = true;
844 out.close();
845 }
846
847 /**
848 *
849 *
850 * @param bool
851 */
852 public void setSignalling(boolean bool) {
853 acousticSignalling = bool;
854 }
855
856 /**
857 *
858 *
859 * @return
860 */
861 public boolean isSignalling() {
862 return acousticSignalling;
863 }
864
865 /**
866 *
867 *
868 * @throws IOException
869 */
870 public void bell() throws IOException {
871 if (acousticSignalling) {
872 write(BEL);
873 }
874
875 if (autoflush) {
876 flush();
877 }
878 }
879
880 /**
881 *
882 *
883 * @param topmargin
884 * @param bottommargin
885 *
886 * @return
887 *
888 * @throws IOException
889 */
890 public boolean defineScrollRegion(int topmargin, int bottommargin)
891 throws IOException {
892 if (terminal.supportsScrolling()) {
893 write(terminal.getScrollMarginsSequence(topmargin, bottommargin));
894 flush();
895
896 return true;
897 } else {
898 return false;
899 }
900 }
901
902 /**
903 *
904 *
905 * @param color
906 *
907 * @throws IOException
908 */
909 public void setForegroundColor(int color) throws IOException {
910 if (terminal.supportsSGR()) {
911 write(terminal.getGRSequence(FCOLOR, color));
912
913 if (autoflush) {
914 flush();
915 }
916 }
917 }
918
919 /**
920 *
921 *
922 * @param color
923 *
924 * @throws IOException
925 */
926 public void setBackgroundColor(int color) throws IOException {
927 if (terminal.supportsSGR()) {
928 //this method adds the offset to the fg color by itself
929 write(terminal.getGRSequence(BCOLOR, color + 10));
930
931 if (autoflush) {
932 flush();
933 }
934 }
935 }
936
937 /**
938 *
939 *
940 * @param b
941 *
942 * @throws IOException
943 */
944 public void setBold(boolean b) throws IOException {
945 if (terminal.supportsSGR()) {
946 if (b) {
947 write(terminal.getGRSequence(STYLE, BOLD));
948 } else {
949 write(terminal.getGRSequence(STYLE, BOLD_OFF));
950 }
951
952 if (autoflush) {
953 flush();
954 }
955 }
956 }
957
958 /**
959 *
960 *
961 * @param b
962 *
963 * @throws IOException
964 */
965 public void setUnderlined(boolean b) throws IOException {
966 if (terminal.supportsSGR()) {
967 if (b) {
968 write(terminal.getGRSequence(STYLE, UNDERLINED));
969 } else {
970 write(terminal.getGRSequence(STYLE, UNDERLINED_OFF));
971 }
972
973 if (autoflush) {
974 flush();
975 }
976 }
977 }
978
979 /**
980 *
981 *
982 * @param b
983 *
984 * @throws IOException
985 */
986 public void setItalic(boolean b) throws IOException {
987 if (terminal.supportsSGR()) {
988 if (b) {
989 write(terminal.getGRSequence(STYLE, ITALIC));
990 } else {
991 write(terminal.getGRSequence(STYLE, ITALIC_OFF));
992 }
993
994 if (autoflush) {
995 flush();
996 }
997 }
998 }
999
1000 /**
1001 *
1002 *
1003 * @param b
1004 *
1005 * @throws IOException
1006 */
1007 public void setBlink(boolean b) throws IOException {
1008 if (terminal.supportsSGR()) {
1009 if (b) {
1010 write(terminal.getGRSequence(STYLE, BLINK));
1011 } else {
1012 write(terminal.getGRSequence(STYLE, BLINK_OFF));
1013 }
1014
1015 if (autoflush) {
1016 flush();
1017 }
1018 }
1019 }
1020
1021 /**
1022 *
1023 *
1024 * @throws IOException
1025 */
1026 public void resetAttributes() throws IOException {
1027 if (terminal.supportsSGR()) {
1028 write(terminal.getGRSequence(RESET, 0));
1029 }
1030 }
1031
1032 private int handleEscapeSequence(int i) throws IOException {
1033 if (i == ESCAPE) {
1034 int[] bytebuf = new int[terminal.getAtomicSequenceLength()];
1035
1036 //fill atomic length
1037 //FIXME: ensure CAN, broken Escapes etc.
1038 for (int m = 0; m < bytebuf.length; m++) {
1039 bytebuf[m] = read();
1040 }
1041
1042 return terminal.translateEscapeSequence(bytebuf);
1043 }
1044
1045 if (i == BYTEMISSING) {
1046 //FIXME:longer escapes etc...
1047 }
1048
1049 return HANDLED;
1050 }
1051
1052 /**
1053 *
1054 *
1055 * @return
1056 */
1057 public boolean isAutoflushing() {
1058 return autoflush;
1059 }
1060
1061 /**
1062 *
1063 *
1064 * @param b
1065 */
1066 public void setAutoflushing(boolean b) {
1067 autoflush = b;
1068 }
1069
1070 /**
1071 *
1072 *
1073 * @throws IOException
1074 */
1075 public void close() throws IOException {
1076 closeOutput();
1077 }
1078
1079 /**
1080 *
1081 *
1082 * @return
1083 */
1084 public Terminal getTerminal() {
1085 return terminal;
1086 }
1087
1088 //getTerminal
1089 public void setDefaultTerminal() throws IOException {
1090 //set the terminal passing the negotiated string
1091 setTerminal(term);
1092 }
1093
1094 /**
1095 *
1096 *
1097 * @param terminalName
1098 *
1099 * @throws IOException
1100 */
1101 public void setTerminal(String terminalName) throws IOException {
1102 terminal = TerminalFactory.newInstance(terminalName);
1103
1104 //Terminal is set we init it....
1105 initTerminal();
1106 }
1107
1108 private void initTerminal() throws IOException {
1109 write(terminal.getInitSequence());
1110 flush();
1111 }
1112
1113 /**
1114 *
1115 *
1116 * @return
1117 */
1118 public int getRows() {
1119 return rows;
1120 }
1121
1122 /**
1123 *
1124 *
1125 * @return
1126 */
1127 public int getColumns() {
1128 return cols;
1129 }
1130 }
1131
1132
1133 //class TerminalIO