/*
* Copyright 2010 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.ssh;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import org.apache.mina.core.filterchain.IoFilter.NextFilter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.sshd.common.SshException;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.krakenapps.ansicode.AnsiEscapeCode;
import org.krakenapps.api.ScriptOutputStream;
import org.krakenapps.api.TelnetCommand;
import org.krakenapps.console.ConsoleInputStream;
import org.krakenapps.console.QuitHandler;
import org.krakenapps.console.ShellSession;
import org.krakenapps.console.TelnetStateMachine;
import org.krakenapps.main.Kraken;
import org.krakenapps.script.ScriptContextImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SshShell implements Command, Runnable, QuitHandler {
private final Logger logger = LoggerFactory.getLogger(SshShell.class.getName());
private ShellSession session;
private InputStream in;
private ExitCallback callback;
private Thread thread;
private TelnetStateMachine tsm;
private ScriptContextImpl context;
public SshShell() {
context = new ScriptContextImpl(Kraken.getContext(), this);
session = new ShellSession(context);
tsm = new TelnetStateMachine(new MessageReceiver(session), context);
}
@Override
public void onQuit() {
thread.interrupt();
}
@Override
public void setInputStream(InputStream in) {
this.in = in;
session.getScriptContext().setInputStream(new ConsoleInputStream(session.getScriptContext()));
}
@Override
public void setOutputStream(OutputStream out) {
session.getScriptContext().setOutputStream(new SshOutputStream(out));
}
@Override
public void setErrorStream(OutputStream err) {
}
@Override
public void setExitCallback(ExitCallback callback) {
this.callback = callback;
}
@Override
public void start(Environment env) throws IOException {
int width = Integer.parseInt(env.getEnv().get(Environment.ENV_COLUMNS));
int height = Integer.parseInt(env.getEnv().get(Environment.ENV_LINES));
context.setWindowSize(width, height);
String username = env.getEnv().get(Environment.ENV_USER);
session.setPrincipal(username);
thread = new Thread(this, "SshShell");
thread.start();
}
@Override
public void destroy() {
thread.interrupt();
}
@Override
public void run() {
session.printBanner();
context.printPrompt();
try {
for (;;) {
byte b = (byte) in.read();
tsm.feed(b);
}
} catch (Exception e) {
if (!(e instanceof InterruptedException))
e.printStackTrace();
} finally {
callback.onExit(0);
}
}
static class MessageReceiver implements ProtocolDecoderOutput {
private ShellSession session;
public MessageReceiver(ShellSession session) {
this.session = session;
}
@Override
public void flush(NextFilter nextFilter, IoSession session) {
}
@Override
public void write(Object message) {
try {
session.handleMessage(message);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SshOutputStream implements ScriptOutputStream {
private OutputStream out;
public SshOutputStream(OutputStream out) {
this.out = out;
}
@Override
public ScriptOutputStream print(AnsiEscapeCode code) {
try {
out.write(code.toByteArray());
out.flush();
} catch (Exception e) {
logger.error("kraken core: print error", e);
}
return this;
}
@Override
public ScriptOutputStream printf(String format, Object... args) {
try {
String text = String.format(format, args);
text = text.replaceAll("\n", "\r\n");
byte[] b = text.getBytes("utf-8");
out.write(b);
out.flush();
} catch (UnsupportedEncodingException e) {
logger.error("kraken core: printf error", e);
} catch (IOException e) {
logger.error("kraken core: printf error", e);
}
return this;
}
@Override
public ScriptOutputStream print(String value) {
try {
byte[] b = value.getBytes("utf-8");
out.write(b);
out.flush();
} catch (IOException e) {
logger.error("kraken core: print error", e);
if (e instanceof SshException && e.getMessage().equals("Already closed")) {
throw new IllegalStateException("SSH: Already Closed");
}
}
return this;
}
@Override
public ScriptOutputStream println(String value) {
print(value);
print("\r\n");
return this;
}
@Override
public ScriptOutputStream print(TelnetCommand command) {
try {
out.write(TelnetCommand.InterpretAsControl);
out.write(command.toByteArray());
} catch (IOException e) {
logger.error("kraken core: print error", e);
}
return this;
}
}
}