/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.files.ssh;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*
*/
public abstract class AbstractTerminalEmulator extends Thread implements TerminalEmulator {
private static final Logger logger = LoggerFactory.getLogger(AbstractTerminalEmulator.class.getName());
protected final StringBuilder lineBuffer = new StringBuilder();
protected TerminalHandler rootTerminalHandler = null;
protected TerminalHandler terminalHandler = null;
protected Reader reader = null;
protected Writer writer = null;
protected boolean running = false;
protected boolean echo = true;
protected int cursorPosition = 0;
protected int lineLength = 0;
protected int commandBufferIndex = 0;
protected int tabCount = 0;
public AbstractTerminalEmulator(final InputStream in, final OutputStream out, final TerminalHandler rootTerminalHandler) {
this.rootTerminalHandler = rootTerminalHandler;
this.terminalHandler = rootTerminalHandler;
this.reader = new InputStreamReader(in);
this.writer = new OutputStreamWriter(out);
}
@Override
public void stopEmulator() {
this.running = false;
}
@Override
public void setTerminalHandler(final TerminalHandler handler) throws IOException {
this.terminalHandler = handler;
}
@Override
public void restoreRootTerminalHandler() throws IOException {
this.terminalHandler = rootTerminalHandler;
setEcho(true);
}
@Override
public void handleCursorUp() throws IOException {
final List<String> commandHistory = terminalHandler.getCommandHistory();
if (commandHistory != null && echo) {
final int commandBufferSize = commandHistory.size();
if (commandBufferIndex >= 0 && commandBufferIndex < commandBufferSize) {
displaySelectedCommand(commandHistory.get(commandBufferSize - commandBufferIndex - 1));
if (commandBufferIndex < commandBufferSize - 1) {
commandBufferIndex++;
}
}
}
}
@Override
public void handleCursorDown() throws IOException {
final List<String> commandHistory = terminalHandler.getCommandHistory();
if (commandHistory != null && echo) {
if (commandBufferIndex > 0) {
final int commandBufferSize = commandHistory.size();
if (commandBufferIndex >= 0 && commandBufferIndex <= commandBufferSize) {
commandBufferIndex--;
displaySelectedCommand(commandHistory.get(commandBufferSize - commandBufferIndex - 1));
}
} else {
displaySelectedCommand("");
}
}
}
@Override
public void handleCtrlKey(final int key) throws IOException {
// 0 is Ctrl-A, 1 is Ctrl-B, etc..
switch (key) {
case 3:
terminalHandler.handleCtrlC();
break;
case 4:
if (lineLength == 0) {
terminalHandler.handleLogoutRequest();
}
break;
}
}
@Override
public void run() {
running = true;
while (running) {
try {
int c = reader.read();
// global tab count
if (c == 9) {
tabCount++;
} else {
tabCount = 0;
}
switch (c) {
case 9:
handleTab(tabCount);
break;
case 13:
handleNewline();
break;
case 27:
// escape sequence
c = reader.read();
switch (c) {
case 91:
// cursor keys
c = reader.read();
switch (c) {
case 50:
// insert
c = reader.read();
switch (c) {
case 126:
handleInsert();
break;
}
break;
case 51:
// delete
c = reader.read();
switch (c) {
case 126:
handleDelete();
break;
}
break;
case 53:
// page up
c = reader.read();
switch (c) {
case 126:
handlePageUp();
break;
}
break;
case 54:
// page down
c = reader.read();
switch (c) {
case 126:
handlePageDown();
break;
}
break;
case 65:
// up
handleCursorUp();
break;
case 66:
// down
handleCursorDown();
break;
case 67:
handleCursorRight();
break;
case 68:
handleCursorLeft();
break;
case 70:
handleEnd();
break;
case 72:
handleHome();
break;
case 90:
handleShiftTab();
break;
}
break;
}
break;
case 127:
handleBackspace();
break;
default:
if (c < 27) {
handleCtrlKey(c);
} else {
// read unicode character
handleCharacter(c);
}
break;
}
writer.flush();
} catch (Throwable t) {
logger.warn("", t);
if (writer != null) {
try {
writer.write('\n');
writer.write(t.getMessage());
writer.write('\n');
} catch (Throwable t2) {
logger.warn("", t);
}
}
}
}
terminalHandler.handleExit();
}
@Override
public void print(final Object... text) throws IOException {
if (text != null) {
for (final Object o : text) {
if (o != null) {
writer.write(o.toString().replaceAll("\n", "\r\n"));
} else {
writer.write("null");
}
}
}
writer.flush();
}
@Override
public void println(final Object... text) throws IOException {
print(text);
println();
writer.flush();
}
@Override
public void clearLineBuffer() {
lineBuffer.setLength(0);
cursorPosition = 0;
lineLength = 0;
}
@Override
public StringBuilder getLineBuffer() {
return lineBuffer;
}
@Override
public void setEcho(final boolean echo) {
this.echo = echo;
}
@Override
public void flush() throws IOException {
writer.flush();
}
@Override
public void clearTabCount() {
tabCount = 0;
}
// ----- protected methods -----
protected void handleLineInternal(final String line) throws IOException {
terminalHandler.handleLine(line);
commandBufferIndex = 0;
}
// ----- private methods -----
private void displaySelectedCommand(final String selectedCommand) throws IOException {
lineBuffer.setLength(0);
lineBuffer.append(selectedCommand);
lineLength = lineBuffer.length();
int loopCount = cursorPosition;
for (int i=0; i<loopCount; i++) {
handleCursorLeft();
}
writer.write(27);
writer.write('[');
writer.write('K');
print(selectedCommand);
cursorPosition = lineLength;
}
}