├── hostkey.ser ├── src ├── main │ ├── resources │ │ ├── io │ │ │ └── termd │ │ │ │ └── core │ │ │ │ ├── http │ │ │ │ ├── logo.png │ │ │ │ ├── fullsc.png │ │ │ │ └── main.css │ │ │ │ └── readline │ │ │ │ └── inputrc │ │ └── META-INF │ │ │ └── services │ │ │ └── io.termd.core.readline.Function │ └── java │ │ └── io │ │ └── termd │ │ └── core │ │ ├── function │ │ ├── Supplier.java │ │ ├── Consumer.java │ │ ├── Function.java │ │ ├── IntConsumer.java │ │ └── BiConsumer.java │ │ ├── readline │ │ ├── KeyEventSupport.java │ │ ├── functions │ │ │ ├── Undo.java │ │ │ ├── BackwardKillLine.java │ │ │ ├── BeginningOfLine.java │ │ │ ├── DeleteChar.java │ │ │ ├── ForwardChar.java │ │ │ ├── KillLine.java │ │ │ ├── BackwardChar.java │ │ │ ├── EndOfLine.java │ │ │ ├── BackwardDeleteChar.java │ │ │ ├── Complete.java │ │ │ ├── BackwardKillWord.java │ │ │ ├── NextHistory.java │ │ │ ├── PreviousHistory.java │ │ │ ├── ForwardWord.java │ │ │ ├── BackwardWord.java │ │ │ ├── HistorySearchForward.java │ │ │ └── HistorySearchBackward.java │ │ ├── Functions.java │ │ ├── CompletionStatus.java │ │ ├── KeyEvent.java │ │ ├── Function.java │ │ ├── FunctionEvent.java │ │ └── Keys.java │ │ ├── pty │ │ ├── EventHandler.java │ │ └── Status.java │ │ ├── tty │ │ ├── TtyConnectionSupport.java │ │ ├── TtyEvent.java │ │ ├── TtyOutputMode.java │ │ ├── ReadBuffer.java │ │ └── TtyEventDecoder.java │ │ ├── term │ │ ├── ElsePart.java │ │ ├── ParserHandler.java │ │ ├── TermInfo.java │ │ ├── EvalContext.java │ │ ├── Sequence.java │ │ ├── Device.java │ │ └── Feature.java │ │ ├── ssh │ │ └── netty │ │ │ ├── AsyncUserAuthServiceFactory.java │ │ │ ├── AsyncAuth.java │ │ │ ├── NettyIoWriteFuture.java │ │ │ ├── Helper.java │ │ │ ├── NettyIoService.java │ │ │ ├── NettyIoHandlerBridge.java │ │ │ ├── NettyIoServiceFactoryFactory.java │ │ │ └── NettyIoServiceFactory.java │ │ ├── util │ │ ├── LineBufferUtils.java │ │ ├── CompletableFuture.java │ │ ├── Logging.java │ │ └── Vector.java │ │ ├── telnet │ │ ├── TelnetHandler.java │ │ ├── netty │ │ │ ├── NettyTelnetConnection.java │ │ │ └── TelnetChannelHandler.java │ │ └── TelnetBootstrap.java │ │ ├── io │ │ ├── BinaryEncoder.java │ │ └── TelnetCharset.java │ │ └── http │ │ └── netty │ │ └── TtyServerInitializer.java ├── test │ ├── resources │ │ └── simplelogger.properties │ └── java │ │ ├── io │ │ └── termd │ │ │ └── core │ │ │ ├── telnet │ │ │ ├── TelnetTestBase.java │ │ │ ├── NettyTelnetTermTest.java │ │ │ ├── NettyTelnetHandlerTest.java │ │ │ └── TelnetTermTest.java │ │ │ ├── ssh │ │ │ ├── NettySshdTestBase.java │ │ │ ├── TestIoServiceFactoryFactory.java │ │ │ ├── TestServiceFactory.java │ │ │ ├── AsyncAuthTest.java │ │ │ └── AsyncAuthInteractiveTest.java │ │ │ ├── readline │ │ │ ├── KeymapTest.java │ │ │ ├── HistoryTest.java │ │ │ ├── TestTerminal.java │ │ │ └── QuotingTest.java │ │ │ ├── tty │ │ │ ├── DefaultSshTtyTest.java │ │ │ ├── NettyAsciiTelnetTtyTest.java │ │ │ ├── NettyBinaryTelnetTtyTest.java │ │ │ ├── NettyWebsocketTtyTest.java │ │ │ ├── TtyOutputModeTest.java │ │ │ ├── NettySshTtyTest.java │ │ │ └── TelnetTtyTestBase.java │ │ │ ├── http │ │ │ └── websocket │ │ │ │ ├── Configurations.java │ │ │ │ └── server │ │ │ │ ├── TaskStatusUpdateEventDeserializer.java │ │ │ │ └── TaskStatusUpdateEvent.java │ │ │ ├── util │ │ │ ├── ObjectWrapper.java │ │ │ ├── WcwidthTest.java │ │ │ ├── HelperTest.java │ │ │ ├── MockProcess.java │ │ │ └── Wait.java │ │ │ └── io │ │ │ └── TelnetCharsetTest.java │ │ └── org │ │ └── apache │ │ └── sshd │ │ ├── deprecated │ │ ├── UserAuth.java │ │ └── AbstractUserAuth.java │ │ └── util │ │ └── test │ │ ├── EchoShellFactory.java │ │ ├── UnknownCommandFactory.java │ │ ├── BogusPasswordAuthenticator.java │ │ ├── OutputCountTrackingOutputStream.java │ │ ├── TeeOutputStream.java │ │ ├── SimpleUserInfo.java │ │ └── JSchLogger.java └── examples │ └── java │ └── examples │ ├── snake │ ├── WebsocketSnakeExample.java │ ├── SshSnakeExample.java │ └── TelnetSnakeExample.java │ ├── plasma │ ├── WebsocketPlasmaExample.java │ ├── SshPlasmaExample.java │ └── TelnetPlasmaExample.java │ ├── shell │ ├── WebsocketShellExample.java │ ├── TelnetShellExample.java │ └── SshShellExample.java │ ├── ptybridge │ ├── WebsocketPtyBridgeExample.java │ ├── SshPtyBridgeExample.java │ └── TelnetPtyBridgeExample.java │ ├── screencast │ ├── WebsocketScreencastingExample.java │ ├── SshScreencastingExample.java │ ├── TelnetScreencastingExample.java │ └── Screencaster.java │ ├── readlinefunction │ ├── ReadlineFunctionExample.java │ ├── ReverseFunction.java │ ├── SshReadlineFunctionExample.java │ ├── TelnetReadlineFunctionExample.java │ └── WebsocketReadlineFunctionExample.java │ ├── readline │ ├── ReadlineExample.java │ ├── SshReadlineExample.java │ ├── WebsocketReadlineExample.java │ └── TelnetReadlineExample.java │ ├── events │ ├── SshEventsExample.java │ ├── TelnetEventsExample.java │ ├── WebsocketEventsExample.java │ └── EventsExample.java │ └── telnet │ └── TelnetExample.java ├── .gitlab-ci.yml └── .gitignore /hostkey.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/termd/HEAD/hostkey.ser -------------------------------------------------------------------------------- /src/main/resources/io/termd/core/http/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/termd/HEAD/src/main/resources/io/termd/core/http/logo.png -------------------------------------------------------------------------------- /src/main/resources/io/termd/core/http/fullsc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/termd/HEAD/src/main/resources/io/termd/core/http/fullsc.png -------------------------------------------------------------------------------- /src/main/java/io/termd/core/function/Supplier.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.function; 2 | 3 | /** 4 | * @author bw on 25/10/2016. 5 | */ 6 | public interface Supplier { 7 | T get(); 8 | } 9 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | build: 6 | stage: build 7 | script: "mvn clean package" 8 | 9 | unit-test: 10 | stage: test 11 | script: "mvn -U test" 12 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/function/Consumer.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.function; 2 | 3 | /** 4 | * @author bw on 25/10/2016. 5 | */ 6 | public interface Consumer { 7 | void accept(T t); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/function/Function.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.function; 2 | 3 | /** 4 | * @author bw on 27/10/2016. 5 | */ 6 | public interface Function { 7 | R apply(T t); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/function/IntConsumer.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.function; 2 | 3 | /** 4 | * @author bw on 25/10/2016. 5 | */ 6 | public interface IntConsumer { 7 | void accept(int value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/function/BiConsumer.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.function; 2 | 3 | /** 4 | * @author bw on 25/10/2016. 5 | */ 6 | public interface BiConsumer { 7 | void accept(T t, U u); 8 | } 9 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.showThreadName=true 2 | org.slf4j.simpleLogger.showShortLogName=false 3 | org.slf4j.simpleLogger.levelInBrackets=true 4 | org.slf4j.simpleLogger.showDateTime=true 5 | org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss:SSS 6 | org.slf4j.simpleLogger.defaultLogLevel=error 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .extract 2 | .DS_Store 3 | target 4 | src/main/webapp/dist 5 | src/main/webapp/node_modules 6 | *.releaseBackup 7 | release.properties 8 | .idea 9 | *.iml 10 | derby.log 11 | nul 12 | .classpath 13 | .project 14 | org.eclipse.m2e.core.prefs 15 | org.eclipse.core.resources.prefs 16 | org.eclipse.jdt.core.prefs 17 | dependency-reduced-pom.xml 18 | .settings 19 | -------------------------------------------------------------------------------- /src/main/resources/io/termd/core/http/main.css: -------------------------------------------------------------------------------- 1 | #terminal:-webkit-full-screen{ 2 | background-color: rgb(255, 255, 12); 3 | } 4 | .container { 5 | width: 100%; 6 | min-height: 600px; 7 | } 8 | .fullSc { 9 | z-index: 10000; 10 | position: fixed; 11 | top: 25%; 12 | left: 90%; 13 | display: none; 14 | } 15 | #fullScBtn { 16 | border-radius:17px; 17 | border: 0; 18 | cursor: pointer; 19 | background-color: black; 20 | } -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/KeyEventSupport.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline; 2 | 3 | import java.nio.IntBuffer; 4 | 5 | /** 6 | * @author bw on 26/10/2016. 7 | */ 8 | public abstract class KeyEventSupport implements KeyEvent { 9 | @Override 10 | public IntBuffer buffer() { 11 | int length = length(); 12 | IntBuffer buf = IntBuffer.allocate(length); 13 | for (int i = 0; i < length; i++) { 14 | buf.put(getCodePointAt(i)); 15 | } 16 | buf.flip(); 17 | return buf; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/io/termd/core/readline/inputrc: -------------------------------------------------------------------------------- 1 | "\e[D": backward-char 2 | "\e[C": forward-char 3 | "\e[B": next-history 4 | "\e[A": previous-history 5 | "\C-?": backward-delete-char 6 | "\C-h": backward-delete-char 7 | "\C-X\C-U": undo 8 | "\eb": backward-word 9 | "\C-e": end-of-line 10 | "\C-a": beginning-of-line 11 | "\C-D": delete-char 12 | "\e[3~": delete-char 13 | "\C-i": complete 14 | "\C-j": accept-line 15 | "\C-m": accept-line 16 | "\C-k": kill-line 17 | "\eb": backward-word 18 | "\ef": forward-word 19 | "\e\C-?": backward-kill-word 20 | "\C-x[3~": backward-kill-line 21 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/pty/EventHandler.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.pty; 2 | 3 | import io.termd.core.function.BiConsumer; 4 | import io.termd.core.tty.TtyEvent; 5 | 6 | /** 7 | * @author bw on 25/10/2016. 8 | */ 9 | public class EventHandler implements BiConsumer { 10 | private PtyMaster task; 11 | 12 | public EventHandler(PtyMaster task) { 13 | this.task = task; 14 | } 15 | 16 | @Override 17 | public void accept(TtyEvent event, Integer integer) { 18 | if (event == TtyEvent.INTR) { 19 | task.interruptProcess(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/examples/java/examples/snake/WebsocketSnakeExample.java: -------------------------------------------------------------------------------- 1 | package examples.snake; 2 | 3 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class WebsocketSnakeExample { 8 | 9 | public synchronized static void main(String[] args) throws Throwable { 10 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 11 | bootstrap.start(new SnakeGame()).get(10, TimeUnit.SECONDS); 12 | System.out.println("Web server started on localhost:8080"); 13 | WebsocketSnakeExample.class.wait(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/examples/java/examples/plasma/WebsocketPlasmaExample.java: -------------------------------------------------------------------------------- 1 | package examples.plasma; 2 | 3 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class WebsocketPlasmaExample { 8 | 9 | public synchronized static void main(String[] args) throws Throwable { 10 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 11 | bootstrap.start(new Plasma()).get(10, TimeUnit.SECONDS); 12 | System.out.println("Web server started on localhost:8080"); 13 | WebsocketPlasmaExample.class.wait(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/tty/TtyConnectionSupport.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.tty; 2 | 3 | import io.termd.core.util.Helper; 4 | 5 | /** 6 | * @author bw on 25/10/2016. 7 | */ 8 | public abstract class TtyConnectionSupport implements TtyConnection { 9 | @Override 10 | public void close(int exit) { 11 | close(); 12 | } 13 | 14 | /** 15 | * Write a string to the client. 16 | * 17 | * @param s the string to write 18 | */ 19 | @Override 20 | public TtyConnection write(String s) { 21 | int[] codePoints = Helper.toCodePoints(s); 22 | stdoutHandler().accept(codePoints); 23 | return this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/Undo.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline.functions; 2 | 3 | import io.termd.core.readline.Function; 4 | import io.termd.core.readline.LineBuffer; 5 | import io.termd.core.readline.Readline; 6 | 7 | /** 8 | * @author bw on 02/11/2016. 9 | */ 10 | public class Undo implements Function { 11 | @Override 12 | public String name() { 13 | return "undo"; 14 | } 15 | 16 | @Override 17 | public void apply(Readline.Interaction interaction) { 18 | LineBuffer buf = interaction.buffer().copy(); 19 | buf.setSize(0); 20 | interaction.refresh(buf); 21 | interaction.resume(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/telnet/TelnetTestBase.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.telnet; 2 | 3 | import io.termd.core.TestBase; 4 | import io.termd.core.function.Function; 5 | import io.termd.core.function.Supplier; 6 | import org.junit.Rule; 7 | 8 | import java.io.Closeable; 9 | 10 | /** 11 | * @author Julien Viet 12 | */ 13 | public abstract class TelnetTestBase extends TestBase { 14 | 15 | @Rule 16 | public TelnetServerRule server = new TelnetServerRule(serverFactory()); 17 | 18 | @Rule 19 | public TelnetClientRule client = new TelnetClientRule(); 20 | 21 | protected abstract Function, Closeable> serverFactory(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/examples/java/examples/shell/WebsocketShellExample.java: -------------------------------------------------------------------------------- 1 | package examples.shell; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 5 | import io.termd.core.tty.TtyConnection; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class WebsocketShellExample { 10 | 11 | public synchronized static void main(String[] args) throws Throwable { 12 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 13 | bootstrap.start(new Shell()).get(10, TimeUnit.SECONDS); 14 | System.out.println("Web server started on localhost:8080"); 15 | WebsocketShellExample.class.wait(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BackwardKillLine.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline.functions; 2 | 3 | import io.termd.core.readline.Function; 4 | import io.termd.core.readline.LineBuffer; 5 | import io.termd.core.readline.Readline; 6 | 7 | /** 8 | * @author wangtao 2016-12-15 15:12. 9 | */ 10 | public class BackwardKillLine implements Function { 11 | 12 | @Override 13 | public String name() { 14 | return "backward-kill-line"; 15 | } 16 | 17 | @Override 18 | public void apply(Readline.Interaction interaction) { 19 | LineBuffer buf = interaction.buffer().copy(); 20 | buf.delete(-buf.getCursor()); 21 | interaction.refresh(buf); 22 | interaction.resume(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/ssh/NettySshdTestBase.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.ssh; 2 | 3 | import org.apache.sshd.common.io.IoServiceFactoryFactory; 4 | import org.junit.After; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | 8 | /** 9 | * @author Julien Viet 10 | */ 11 | public class NettySshdTestBase extends Assert { 12 | 13 | @Before 14 | public void setNettyServer() { 15 | // System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace"); 16 | System.setProperty(IoServiceFactoryFactory.class.getName(), TestIoServiceFactoryFactory.class.getName()); 17 | } 18 | 19 | @After 20 | public void unsetNettyServer() { 21 | System.clearProperty(IoServiceFactoryFactory.class.getName()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/Functions.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline; 2 | 3 | import io.termd.core.util.Helper; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @author bw on 25/10/2016. 10 | */ 11 | public class Functions { 12 | /** 13 | * Load the defaults function via the {@link java.util.ServiceLoader} SPI. 14 | * 15 | * @return the loaded function 16 | */ 17 | public static List loadDefaults() { 18 | List functions = new ArrayList(); 19 | for (io.termd.core.readline.Function function : Helper.loadServices(Thread.currentThread().getContextClassLoader(), io.termd.core.readline.Function.class)) { 20 | functions.add(function); 21 | } 22 | return functions; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/ElsePart.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | public interface ElsePart { 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/AsyncUserAuthServiceFactory.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.ssh.netty; 2 | 3 | import org.apache.sshd.common.Service; 4 | import org.apache.sshd.common.ServiceFactory; 5 | import org.apache.sshd.common.session.Session; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @author Julien Viet 11 | */ 12 | public class AsyncUserAuthServiceFactory implements ServiceFactory { 13 | public static final AsyncUserAuthServiceFactory INSTANCE = new AsyncUserAuthServiceFactory(); 14 | 15 | public AsyncUserAuthServiceFactory() { 16 | super(); 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return "ssh-userauth"; 22 | } 23 | 24 | @Override 25 | public Service create(Session session) throws IOException { 26 | return new AsyncUserAuthService(session); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.termd.core.readline.Function: -------------------------------------------------------------------------------- 1 | io.termd.core.readline.functions.BackwardChar 2 | io.termd.core.readline.functions.BackwardKillWord 3 | io.termd.core.readline.functions.BackwardWord 4 | io.termd.core.readline.functions.ForwardChar 5 | io.termd.core.readline.functions.ForwardWord 6 | io.termd.core.readline.functions.BackwardDeleteChar 7 | io.termd.core.readline.functions.PreviousHistory 8 | io.termd.core.readline.functions.EndOfLine 9 | io.termd.core.readline.functions.NextHistory 10 | io.termd.core.readline.functions.BeginningOfLine 11 | io.termd.core.readline.functions.DeleteChar 12 | io.termd.core.readline.functions.Complete 13 | io.termd.core.readline.functions.KillLine 14 | io.termd.core.readline.functions.Undo 15 | io.termd.core.readline.functions.BackwardKillLine 16 | io.termd.core.readline.functions.HistorySearchBackward 17 | io.termd.core.readline.functions.HistorySearchForward -------------------------------------------------------------------------------- /src/examples/java/examples/ptybridge/WebsocketPtyBridgeExample.java: -------------------------------------------------------------------------------- 1 | package examples.ptybridge; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 5 | import io.termd.core.pty.TtyBridge; 6 | import io.termd.core.tty.TtyConnection; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class WebsocketPtyBridgeExample { 11 | 12 | public synchronized static void main(String[] args) throws Throwable { 13 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 14 | bootstrap.start(new Consumer() { 15 | @Override 16 | public void accept(TtyConnection conn) { 17 | new TtyBridge(conn).readline(); 18 | } 19 | }).get(10, TimeUnit.SECONDS); 20 | System.out.println("Web server started on localhost:8080"); 21 | WebsocketPtyBridgeExample.class.wait(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/readline/KeymapTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * @author Julien Viet 9 | */ 10 | public class KeymapTest { 11 | 12 | @Test 13 | public void bindFunction1() { 14 | Keymap keymap = new Keymap(); 15 | keymap.bindFunction("\\C-j", "my-func"); 16 | EventQueue eq = new EventQueue(keymap); 17 | eq.append('J' - 64); 18 | assertEquals("my-func", ((FunctionEvent) eq.next()).name()); 19 | assertFalse(eq.hasNext()); 20 | } 21 | 22 | @Test 23 | public void bindFunction2() { 24 | Keymap keymap = new Keymap(); 25 | keymap.bindFunction(new int[]{'J' - 64}, "my-func"); 26 | EventQueue eq = new EventQueue(keymap); 27 | eq.append('J' - 64); 28 | assertEquals("my-func", ((FunctionEvent) eq.next()).name()); 29 | assertFalse(eq.hasNext()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/CompletionStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | enum CompletionStatus { 23 | 24 | PENDING, 25 | 26 | INLINING, 27 | 28 | COMPLETING, 29 | 30 | COMPLETED 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/examples/java/examples/screencast/WebsocketScreencastingExample.java: -------------------------------------------------------------------------------- 1 | package examples.screencast; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 5 | import io.termd.core.tty.TtyConnection; 6 | 7 | import java.awt.*; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class WebsocketScreencastingExample { 11 | 12 | public synchronized static void main(String[] args) throws Throwable { 13 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 14 | final Robot robot = new Robot(); 15 | bootstrap.start(new Consumer() { 16 | @Override 17 | public void accept(TtyConnection conn) { 18 | new Screencaster(robot, conn).handle(); 19 | } 20 | }).get(10, TimeUnit.SECONDS); 21 | System.out.println("Web server started on localhost:8080"); 22 | WebsocketScreencastingExample.class.wait(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/examples/java/examples/readlinefunction/ReadlineFunctionExample.java: -------------------------------------------------------------------------------- 1 | package examples.readlinefunction; 2 | 3 | import examples.readline.ReadlineExample; 4 | import io.termd.core.readline.Function; 5 | import io.termd.core.readline.Functions; 6 | import io.termd.core.readline.Keymap; 7 | import io.termd.core.readline.Readline; 8 | import io.termd.core.tty.TtyConnection; 9 | 10 | /** 11 | * Shows how to extend readline with custom functions. 12 | */ 13 | public class ReadlineFunctionExample { 14 | 15 | public static void handle(TtyConnection conn) { 16 | 17 | // The reverse function simply reverse the edit buffer 18 | Function reverseFunction = new ReverseFunction(); 19 | 20 | ReadlineExample.readline( 21 | // Bind reverse to Ctrl-g to the reverse function 22 | new Readline(Keymap.getDefault().bindFunction("\\C-g", "reverse")). 23 | addFunctions(Functions.loadDefaults()).addFunction(reverseFunction), 24 | conn); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/examples/java/examples/readlinefunction/ReverseFunction.java: -------------------------------------------------------------------------------- 1 | package examples.readlinefunction; 2 | 3 | import io.termd.core.readline.Function; 4 | import io.termd.core.readline.LineBuffer; 5 | import io.termd.core.readline.Readline; 6 | 7 | /** 8 | * @author Julien Viet 9 | */ 10 | public class ReverseFunction implements Function { 11 | 12 | @Override 13 | public String name() { 14 | return "reverse"; 15 | } 16 | 17 | @Override 18 | public void apply(Readline.Interaction interaction) { 19 | int[] points = interaction.buffer().toArray(); 20 | 21 | // Reverse the buffer 22 | for (int i = 0; i < points.length / 2; i++) { 23 | int temp = points[i]; 24 | points[i] = points[points.length - 1 - i]; 25 | points[points.length - 1 - i] = temp; 26 | } 27 | 28 | // Refresh buffer 29 | interaction.refresh(new LineBuffer().insert(points)); 30 | 31 | // Resume readline 32 | interaction.resume(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/AsyncAuth.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.ssh.netty; 2 | 3 | 4 | import io.termd.core.function.Consumer; 5 | 6 | /** 7 | * @author Julien Viet 8 | */ 9 | public class AsyncAuth extends RuntimeException { 10 | 11 | private volatile Consumer listener; 12 | private volatile Boolean authed; 13 | 14 | public void setAuthed(boolean authed) { 15 | Consumer listener; 16 | synchronized (this) { 17 | if (this.authed != null) { 18 | return; 19 | } 20 | this.authed = authed; 21 | listener = this.listener; 22 | } 23 | if (listener != null) { 24 | listener.accept(authed); 25 | } 26 | } 27 | 28 | public void setListener(Consumer listener) { 29 | Boolean result; 30 | synchronized (this) { 31 | this.listener = listener; 32 | result = this.authed; 33 | } 34 | if (result != null) { 35 | listener.accept(result); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/NettyIoWriteFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import org.apache.sshd.common.io.AbstractIoWriteFuture; 20 | 21 | /** 22 | * @author Julien Viet 23 | */ 24 | public class NettyIoWriteFuture extends AbstractIoWriteFuture { 25 | 26 | public NettyIoWriteFuture() { 27 | super(null); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/KeyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | import java.nio.IntBuffer; 20 | 21 | /** 22 | * A key event. 23 | * 24 | * @author Julien Viet 25 | */ 26 | public interface KeyEvent { 27 | 28 | IntBuffer buffer(); 29 | 30 | int getCodePointAt(int index) throws IndexOutOfBoundsException; 31 | 32 | int length(); 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/DefaultSshTtyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import org.apache.sshd.server.SshServer; 20 | 21 | /** 22 | * @author Julien Viet 23 | */ 24 | public class DefaultSshTtyTest extends SshTtyTestBase { 25 | 26 | @Override 27 | protected SshServer createServer() { 28 | return SshServer.setUpDefaultServer(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/http/websocket/Configurations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.http.websocket; 18 | 19 | /** 20 | * @author Matej Lazar 21 | */ 22 | public class Configurations { 23 | public static final String HOST = "localhost"; 24 | public static final String TERM_PATH = "/term"; 25 | public static final String PROCESS_UPDATES_PATH = "/process-status-updates"; 26 | } 27 | -------------------------------------------------------------------------------- /src/examples/java/examples/readline/ReadlineExample.java: -------------------------------------------------------------------------------- 1 | package examples.readline; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.readline.Functions; 5 | import io.termd.core.readline.Keymap; 6 | import io.termd.core.readline.Readline; 7 | import io.termd.core.tty.TtyConnection; 8 | 9 | /** 10 | * Shows how to use async Readline. 11 | */ 12 | public class ReadlineExample { 13 | 14 | public static void handle(TtyConnection conn) { 15 | readline( 16 | new Readline(Keymap.getDefault()).addFunctions(Functions.loadDefaults()), 17 | conn); 18 | } 19 | 20 | public static void readline(final Readline readline, final TtyConnection conn) { 21 | readline.readline(conn, "% ", new Consumer() { 22 | @Override 23 | public void accept(String line) { 24 | if (line == null) { 25 | conn.write("Logout").close(); 26 | } else { 27 | conn.write("User entered " + line + "\n"); 28 | 29 | // Read line again 30 | readline(readline, conn); 31 | } 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/util/ObjectWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.termd.core.util; 17 | 18 | 19 | /** 20 | * Created by Matej Lazar on 2014-12-09. 21 | */ 22 | public class ObjectWrapper { 23 | private T obj; 24 | 25 | public ObjectWrapper() { 26 | } 27 | 28 | public ObjectWrapper(T obj) { 29 | this.obj = obj; 30 | } 31 | 32 | public void set(T obj) { 33 | this.obj = obj; 34 | } 35 | 36 | public T get() { 37 | return obj; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/tty/TtyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | public enum TtyEvent { 23 | 24 | INTR('C' - 64), 25 | 26 | EOF('D' - 64), 27 | 28 | SUSP('Z' - 64); 29 | 30 | final int codePoint; 31 | 32 | TtyEvent(int codePoint) { 33 | this.codePoint = codePoint; 34 | } 35 | 36 | /** 37 | * @return the default char associated with this event 38 | */ 39 | public int codePoint() { 40 | return codePoint; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/Function.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | /** 20 | * A readline function. 21 | * 22 | * @author Julien Viet 23 | */ 24 | public interface Function { 25 | /** 26 | * The function name, for instance backward-char. 27 | */ 28 | String name(); 29 | 30 | /** 31 | * Apply the function on the current interaction. 32 | * 33 | * @param interaction the current interaction 34 | */ 35 | void apply(Readline.Interaction interaction); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/util/LineBufferUtils.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.util; 2 | 3 | import io.termd.core.readline.LineBuffer; 4 | 5 | /** 6 | * 7 | * @author hengyunabc 2018-11-17 8 | * 9 | */ 10 | public class LineBufferUtils { 11 | public static boolean matchBeforeCursor(LineBuffer buf, int[] line) { 12 | if (line == null) { 13 | return false; 14 | } 15 | 16 | int cursor = buf.getCursor(); 17 | 18 | if (line.length < cursor) { 19 | return false; 20 | } 21 | 22 | for (int i = 0; i < cursor; ++i) { 23 | if (buf.getAt(i) != line[i]) { 24 | return false; 25 | } 26 | } 27 | 28 | return true; 29 | } 30 | 31 | public static boolean equals(LineBuffer buf, int[] line) { 32 | if (line == null) { 33 | return false; 34 | } 35 | 36 | int bufSize = buf.getSize(); 37 | if (bufSize != line.length) { 38 | return false; 39 | } 40 | for (int i = 0; i < bufSize; ++i) { 41 | if (buf.getAt(i) != line[i]) { 42 | return false; 43 | } 44 | } 45 | 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/Helper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import java.io.IOException; 20 | import java.io.InterruptedIOException; 21 | 22 | /** 23 | * @author Julien Viet 24 | */ 25 | class Helper { 26 | 27 | static IOException toIOException(Exception e) { 28 | if (e instanceof InterruptedException) { 29 | InterruptedIOException ioe = new InterruptedIOException(); 30 | ioe.initCause(e); 31 | return ioe; 32 | } else { 33 | return new IOException(e); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/snake/SshSnakeExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package examples.snake; 18 | 19 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | 23 | public class SshSnakeExample { 24 | 25 | public synchronized static void main(String[] args) throws Throwable { 26 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap(). 27 | setPort(5000). 28 | setHost("localhost"); 29 | bootstrap.start(new SnakeGame()).get(10, TimeUnit.SECONDS); 30 | System.out.println("SSH started on localhost:5000"); 31 | SshSnakeExample.class.wait(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/examples/java/examples/snake/TelnetSnakeExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.snake; 17 | 18 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 19 | 20 | import java.util.concurrent.TimeUnit; 21 | 22 | public class TelnetSnakeExample { 23 | 24 | public synchronized static void main(String[] args) throws Throwable { 25 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setHost("localhost").setPort(4000); 26 | bootstrap.start(new SnakeGame()).get(10, TimeUnit.SECONDS); 27 | System.out.println("Telnet server started on localhost:4000"); 28 | TelnetSnakeExample.class.wait(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/examples/java/examples/plasma/SshPlasmaExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package examples.plasma; 18 | 19 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | 23 | public class SshPlasmaExample { 24 | 25 | public synchronized static void main(String[] args) throws Throwable { 26 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap(). 27 | setPort(5000). 28 | setHost("localhost"); 29 | bootstrap.start(new Plasma()).get(10, TimeUnit.SECONDS); 30 | System.out.println("SSH started on localhost:5000"); 31 | SshPlasmaExample.class.wait(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/examples/java/examples/plasma/TelnetPlasmaExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.plasma; 17 | 18 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 19 | 20 | import java.util.concurrent.TimeUnit; 21 | 22 | public class TelnetPlasmaExample { 23 | 24 | public synchronized static void main(String[] args) throws Throwable { 25 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 26 | bootstrap.start(new Plasma()).get(10, TimeUnit.SECONDS); 27 | System.out.println("Telnet server started on localhost:4000"); 28 | TelnetPlasmaExample.class.wait(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/examples/java/examples/shell/TelnetShellExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.shell; 17 | 18 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 19 | 20 | import java.util.concurrent.TimeUnit; 21 | 22 | public class TelnetShellExample { 23 | 24 | public synchronized static void main(String[] args) throws Throwable { 25 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap(). 26 | setHost("localhost"). 27 | setPort(4000); 28 | bootstrap.start(new Shell()).get(10, TimeUnit.SECONDS); 29 | System.out.println("Telnet server started on localhost:4000"); 30 | TelnetShellExample.class.wait(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BeginningOfLine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.Readline; 21 | 22 | /** 23 | * @author Julien Viet 24 | */ 25 | public class BeginningOfLine implements Function { 26 | 27 | @Override 28 | public String name() { 29 | return "beginning-of-line"; 30 | } 31 | 32 | @Override 33 | public void apply(Readline.Interaction interaction) { 34 | interaction.refresh(interaction.buffer().copy().setCursor(0)); 35 | interaction.resume(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/pty/Status.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.pty; 18 | 19 | /** 20 | * @author Matej Lazar 21 | */ 22 | public enum Status { 23 | 24 | NEW (false), 25 | RUNNING (false), 26 | COMPLETED (true), 27 | FAILED (true), 28 | INTERRUPTED (true); 29 | 30 | private final boolean final_; 31 | 32 | Status(boolean finalFlag) { 33 | this.final_ = finalFlag; 34 | } 35 | 36 | /** 37 | * @return true when master won't wait anymore for the process to end, the process may have terminated or not. 38 | */ 39 | public boolean isFinal() { 40 | return final_; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/ParserHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | public class ParserHandler { 23 | 24 | public void beginHeaderLine(String name) {} 25 | 26 | public void addHeader(String name) {} 27 | 28 | public void endHeaderLine() {} 29 | 30 | public void addBooleanFeature(String name, boolean value) {} 31 | 32 | public void addStringFeature(String name, Sequence value) {} 33 | 34 | public void addNumericFeature(String name, int value) {} 35 | 36 | public void endDevice() {} 37 | 38 | public void endDatabase() {} 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/util/WcwidthTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class WcwidthTest { 27 | 28 | @Test 29 | public void testWcwidth() { 30 | assertEquals(2, Wcwidth.of('한')); 31 | assertEquals(2, Wcwidth.of('글')); 32 | assertEquals(1, Wcwidth.of('A')); 33 | assertEquals(0, Wcwidth.of('\0')); 34 | assertEquals(-1, Wcwidth.of('\t')); 35 | assertEquals(0, Wcwidth.of('\u0301')); 36 | assertEquals(1, Wcwidth.of('\u09C0')); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/telnet/NettyTelnetTermTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.telnet; 18 | 19 | import io.termd.core.function.Function; 20 | import io.termd.core.function.Supplier; 21 | 22 | import java.io.Closeable; 23 | 24 | /** 25 | * See Julien Viet 28 | */ 29 | public class NettyTelnetTermTest extends TelnetTermTest { 30 | 31 | @Override 32 | protected Function, Closeable> serverFactory() { 33 | return TelnetServerRule.NETTY_SERVER; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/telnet/NettyTelnetHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.telnet; 18 | 19 | import io.termd.core.function.Function; 20 | import io.termd.core.function.Supplier; 21 | 22 | import java.io.Closeable; 23 | 24 | /** 25 | * See Julien Viet 28 | */ 29 | public class NettyTelnetHandlerTest extends TelnetHandlerTest { 30 | 31 | @Override 32 | protected Function, Closeable> serverFactory() { 33 | return TelnetServerRule.NETTY_SERVER; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/ssh/TestIoServiceFactoryFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh; 18 | 19 | import org.apache.sshd.common.FactoryManager; 20 | import org.apache.sshd.common.io.IoServiceFactory; 21 | import org.apache.sshd.common.io.nio2.Nio2ServiceFactory; 22 | import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory; 23 | 24 | /** 25 | * @author Julien Viet 26 | */ 27 | public class TestIoServiceFactoryFactory extends Nio2ServiceFactoryFactory { 28 | 29 | @Override 30 | public IoServiceFactory create(FactoryManager manager) { 31 | return new TestServiceFactory(manager, getExecutorService(), isShutdownOnExit()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/examples/java/examples/shell/SshShellExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package examples.shell; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 21 | import io.termd.core.tty.TtyConnection; 22 | 23 | import java.util.concurrent.TimeUnit; 24 | 25 | public class SshShellExample { 26 | 27 | public synchronized static void main(String[] args) throws Throwable { 28 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap(). 29 | setPort(5000). 30 | setHost("localhost"); 31 | bootstrap.start(new Shell()).get(10, TimeUnit.SECONDS); 32 | System.out.println("SSH started on localhost:5000"); 33 | SshShellExample.class.wait(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/DeleteChar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class DeleteChar implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "delete-char"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.delete(1); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/ForwardChar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class ForwardChar implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "forward-char"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.moveCursor(1); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/KillLine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class KillLine implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "kill-line"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.setSize(buf.getCursor()); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BackwardChar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class BackwardChar implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "backward-char"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.moveCursor(-1); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/EndOfLine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class EndOfLine implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "end-of-line"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.setCursor(buf.getSize()); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BackwardDeleteChar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class BackwardDeleteChar implements Function { 27 | 28 | @Override 29 | public String name() { 30 | return "backward-delete-char"; 31 | } 32 | 33 | @Override 34 | public void apply(Readline.Interaction interaction) { 35 | LineBuffer buf = interaction.buffer().copy(); 36 | buf.delete(-1); 37 | interaction.refresh(buf); 38 | interaction.resume(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/deprecated/UserAuth.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.deprecated; 20 | 21 | import java.io.IOException; 22 | 23 | import org.apache.sshd.common.util.buffer.Buffer; 24 | 25 | /** 26 | * TODO Add javadoc 27 | * 28 | * @author Apache MINA SSHD Project 29 | */ 30 | public interface UserAuth { 31 | 32 | enum Result { 33 | Success, 34 | Failure, 35 | Continued 36 | } 37 | 38 | Result next(Buffer buffer) throws IOException; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/NettyIoService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import org.apache.sshd.common.io.IoService; 20 | import org.apache.sshd.common.io.IoSession; 21 | import org.apache.sshd.common.util.closeable.AbstractCloseable; 22 | 23 | import java.util.Map; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | import java.util.concurrent.atomic.AtomicLong; 26 | 27 | /** 28 | * @author Julien Viet 29 | */ 30 | public class NettyIoService extends AbstractCloseable implements IoService { 31 | 32 | final AtomicLong sessionSeq = new AtomicLong(); 33 | final Map sessions = new ConcurrentHashMap(); 34 | 35 | @Override 36 | public Map getManagedSessions() { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/util/CompletableFuture.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.util; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | /** 7 | * @author bw on 27/10/2016. 8 | */ 9 | public class CompletableFuture { 10 | private boolean completed; 11 | private T value; 12 | private Throwable ex; 13 | 14 | public synchronized boolean complete(T value) { 15 | this.completed = true; 16 | this.value = value; 17 | notifyAll(); 18 | return true; 19 | } 20 | 21 | public synchronized boolean completeExceptionally(Throwable ex) { 22 | this.completed = true; 23 | this.ex = ex; 24 | notifyAll(); 25 | return true; 26 | } 27 | 28 | public synchronized T get() throws Throwable { 29 | while (!completed) { 30 | wait(); 31 | } 32 | 33 | if (ex != null) { 34 | throw ex; 35 | } else { 36 | return value; 37 | } 38 | } 39 | 40 | public synchronized T get(long timeout, TimeUnit unit) throws Throwable { 41 | while (!completed) { 42 | wait(unit.toMicros(timeout)); 43 | } 44 | 45 | if (!completed) { 46 | throw new TimeoutException(); 47 | } else { 48 | if (ex != null) { 49 | throw ex; 50 | } else { 51 | return value; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/examples/java/examples/events/SshEventsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.events; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class SshEventsExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap().setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | EventsExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | SshEventsExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/NettyIoHandlerBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import org.apache.sshd.common.io.IoHandler; 20 | import org.apache.sshd.common.io.IoSession; 21 | import org.apache.sshd.common.util.Readable; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class NettyIoHandlerBridge { 27 | 28 | public void sessionCreated(IoHandler handler, IoSession session) throws Exception { 29 | handler.sessionCreated(session); 30 | } 31 | 32 | public void sessionClosed(IoHandler handler, IoSession session) throws Exception { 33 | handler.sessionClosed(session); 34 | } 35 | 36 | public void messageReceived(IoHandler handler, IoSession session, Readable message) throws Exception { 37 | handler.messageReceived(session, message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/examples/java/examples/readline/SshReadlineExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readline; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class SshReadlineExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap().setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | SshReadlineExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/EchoShellFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.util.test; 20 | 21 | import org.apache.sshd.common.Factory; 22 | import org.apache.sshd.server.Command; 23 | 24 | /** 25 | * TODO Add javadoc 26 | * 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class EchoShellFactory implements Factory { 30 | public static final EchoShellFactory INSTANCE = new EchoShellFactory(); 31 | 32 | public EchoShellFactory() { 33 | super(); 34 | } 35 | 36 | @Override 37 | public Command create() { 38 | return new EchoShell(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/Complete.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.readline.Completion; 21 | import io.termd.core.readline.Function; 22 | import io.termd.core.readline.Readline; 23 | 24 | /** 25 | * @author Julien Viet 26 | */ 27 | public class Complete implements Function { 28 | 29 | @Override 30 | public String name() { 31 | return "complete"; 32 | } 33 | 34 | @Override 35 | public void apply(Readline.Interaction interaction) { 36 | Consumer handler = interaction.completionHandler(); 37 | if (handler != null) { 38 | Completion completion = new Completion(interaction); 39 | handler.accept(completion); 40 | } else { 41 | interaction.resume(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/examples/java/examples/events/TelnetEventsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.events; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class TelnetEventsExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | EventsExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | TelnetEventsExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/events/WebsocketEventsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.events; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class WebsocketEventsExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | EventsExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | WebsocketEventsExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/readline/WebsocketReadlineExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readline; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class WebsocketReadlineExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | WebsocketReadlineExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/readline/TelnetReadlineExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readline; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class TelnetReadlineExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | TelnetReadlineExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/readlinefunction/SshReadlineFunctionExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readlinefunction; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class SshReadlineFunctionExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap().setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineFunctionExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | SshReadlineFunctionExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BackwardKillWord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * Kill the word behind point. Word boundaries are the same as backward-word. 25 | * 26 | * @author Julien Viet 27 | */ 28 | public class BackwardKillWord implements Function { 29 | 30 | @Override 31 | public String name() { 32 | return "backward-kill-word"; 33 | } 34 | 35 | @Override 36 | public void apply(Readline.Interaction interaction) { 37 | LineBuffer buf = interaction.buffer().copy(); 38 | int cursor = BackwardWord.findPos(buf); 39 | buf.delete(cursor - buf.getCursor()); 40 | interaction.refresh(buf); 41 | interaction.resume(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/util/HelperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.junit.Assert.*; 24 | 25 | /** 26 | * @author Julien Viet 27 | */ 28 | public class HelperTest { 29 | 30 | @Test 31 | public void testFindLongestCommonPrefix() { 32 | assertLongestCommonPrefix(new int[]{}); 33 | assertLongestCommonPrefix(new int[]{0,1,2}, new int[]{0,1,2}); 34 | assertLongestCommonPrefix(new int[]{0,1,2}, new int[]{0,1,2,4}, new int[]{0,1,2,3}); 35 | assertLongestCommonPrefix(new int[]{0,1}, new int[]{0,1,2,4}, new int[]{0,1,2,3}, new int[]{0,1,3}); 36 | } 37 | 38 | private void assertLongestCommonPrefix(int[] expected, int[]... tests) { 39 | assertTrue(Arrays.equals(expected, Helper.findLongestCommonPrefix(Arrays.asList(tests)))); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/examples/java/examples/ptybridge/SshPtyBridgeExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package examples.ptybridge; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.pty.TtyBridge; 21 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 22 | import io.termd.core.tty.TtyConnection; 23 | 24 | import java.util.concurrent.TimeUnit; 25 | 26 | public class SshPtyBridgeExample { 27 | 28 | public synchronized static void main(String[] args) throws Throwable { 29 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap(). 30 | setPort(5000). 31 | setHost("localhost"); 32 | bootstrap.start(new Consumer() { 33 | @Override 34 | public void accept(TtyConnection conn) { 35 | new TtyBridge(conn).readline(); 36 | } 37 | }).get(10, TimeUnit.SECONDS); 38 | System.out.println("SSH started on localhost:5000"); 39 | SshPtyBridgeExample.class.wait(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/examples/java/examples/ptybridge/TelnetPtyBridgeExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.ptybridge; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.pty.TtyBridge; 20 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 21 | import io.termd.core.tty.TtyConnection; 22 | 23 | import java.util.concurrent.TimeUnit; 24 | 25 | public class TelnetPtyBridgeExample { 26 | 27 | public synchronized static void main(String[] args) throws Throwable { 28 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 29 | bootstrap.start(new Consumer() { 30 | @Override 31 | public void accept(TtyConnection conn) { 32 | new TtyBridge(conn).readline(); 33 | } 34 | }).get(10, TimeUnit.SECONDS); 35 | System.out.println("Telnet server started on localhost:4000"); 36 | TelnetPtyBridgeExample.class.wait(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/examples/java/examples/readlinefunction/TelnetReadlineFunctionExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readlinefunction; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class TelnetReadlineFunctionExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineFunctionExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:4000"); 35 | TelnetReadlineFunctionExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/java/examples/readlinefunction/WebsocketReadlineFunctionExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.readlinefunction; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class WebsocketReadlineFunctionExample { 25 | 26 | public synchronized static void main(String[] args) throws Throwable { 27 | NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 28 | bootstrap.start(new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | ReadlineFunctionExample.handle(conn); 32 | } 33 | }).get(10, TimeUnit.SECONDS); 34 | System.out.println("Telnet server started on localhost:8080"); 35 | WebsocketReadlineFunctionExample.class.wait(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/FunctionEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | /** 20 | * A function event. 21 | * 22 | * @author Julien Viet 23 | */ 24 | class FunctionEvent extends KeyEventSupport { 25 | 26 | private final String name; 27 | private final int[] seq; 28 | 29 | public FunctionEvent(String name, int[] seq) { 30 | this.name = name; 31 | this.seq = seq; 32 | } 33 | 34 | /** 35 | * @return the name of the function to apply. 36 | */ 37 | String name() { 38 | return name; 39 | } 40 | 41 | @Override 42 | public int getCodePointAt(int index) throws IndexOutOfBoundsException { 43 | if (index < 0 || index > seq.length) { 44 | throw new IndexOutOfBoundsException("Wrong index: " + index); 45 | } 46 | return seq[index]; 47 | } 48 | 49 | @Override 50 | public int length() { 51 | return seq.length; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/UnknownCommandFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sshd.util.test; 21 | 22 | import org.apache.sshd.server.Command; 23 | import org.apache.sshd.server.CommandFactory; 24 | import org.apache.sshd.server.scp.UnknownCommand; 25 | 26 | /** 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class UnknownCommandFactory implements CommandFactory { 30 | public static final UnknownCommandFactory INSTANCE = new UnknownCommandFactory(); 31 | 32 | public UnknownCommandFactory() { 33 | super(); 34 | } 35 | 36 | @Override 37 | public Command createCommand(String command) { 38 | return new UnknownCommand(command); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/http/websocket/server/TaskStatusUpdateEventDeserializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.http.websocket.server; 18 | 19 | import com.alibaba.fastjson2.JSON; 20 | import com.alibaba.fastjson2.JSONObject; 21 | import io.termd.core.pty.Status; 22 | 23 | import java.io.IOException; 24 | 25 | /** 26 | * @author Matej Lazar 27 | */ 28 | class TaskStatusUpdateEventDeserializer { 29 | 30 | public TaskStatusUpdateEvent deserialize(String jsonStr) throws IOException { 31 | JSONObject node = JSON.parseObject(jsonStr); 32 | 33 | String taskId = node.getString("taskId"); 34 | String oldStatus = node.getString("oldStatus"); 35 | String newStatus = node.getString("newStatus"); 36 | String context = node.getString("context"); 37 | 38 | return new TaskStatusUpdateEvent(taskId, Status.valueOf(oldStatus), Status.valueOf(newStatus), context); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/examples/java/examples/screencast/SshScreencastingExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package examples.screencast; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.ssh.netty.NettySshTtyBootstrap; 21 | import io.termd.core.tty.TtyConnection; 22 | 23 | import java.awt.*; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | public class SshScreencastingExample { 27 | 28 | public synchronized static void main(String[] args) throws Throwable { 29 | NettySshTtyBootstrap bootstrap = new NettySshTtyBootstrap(). 30 | setPort(5000). 31 | setHost("localhost"); 32 | final Robot robot = new Robot(); 33 | bootstrap.start(new Consumer() { 34 | @Override 35 | public void accept(TtyConnection conn) { 36 | new Screencaster(robot, conn).handle(); 37 | } 38 | }).get(10, TimeUnit.SECONDS); 39 | System.out.println("SSH started on localhost:5000"); 40 | SshScreencastingExample.class.wait(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/examples/java/examples/screencast/TelnetScreencastingExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.screencast; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.telnet.netty.NettyTelnetTtyBootstrap; 20 | import io.termd.core.tty.TtyConnection; 21 | 22 | import java.awt.*; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | public class TelnetScreencastingExample { 26 | 27 | public synchronized static void main(String[] args) throws Throwable { 28 | NettyTelnetTtyBootstrap bootstrap = new NettyTelnetTtyBootstrap().setOutBinary(true).setHost("localhost").setPort(4000); 29 | final Robot robot = new Robot(); 30 | bootstrap.start(new Consumer() { 31 | @Override 32 | public void accept(TtyConnection conn) { 33 | new Screencaster(robot, conn).handle(); 34 | } 35 | }).get(10, TimeUnit.SECONDS); 36 | System.out.println("Telnet server started on localhost:4000"); 37 | TelnetScreencastingExample.class.wait(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/NettyAsciiTelnetTtyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.termd.core.function.Function; 20 | import io.termd.core.function.Supplier; 21 | import io.termd.core.telnet.TelnetHandler; 22 | import io.termd.core.telnet.TelnetServerRule; 23 | 24 | import java.io.Closeable; 25 | 26 | /** 27 | * @author Julien Viet 28 | */ 29 | public class NettyAsciiTelnetTtyTest extends TelnetTtyTestBase { 30 | 31 | public NettyAsciiTelnetTtyTest() { 32 | binary = false; 33 | } 34 | 35 | @Override 36 | protected Function, Closeable> serverFactory() { 37 | return TelnetServerRule.NETTY_SERVER; 38 | } 39 | 40 | @Override 41 | protected void assertThreading(Thread connThread, Thread schedulerThread) throws Exception { 42 | assertTrue(connThread.getName().startsWith("nioEventLoopGroup")); 43 | assertTrue(schedulerThread.getName().startsWith("nioEventLoopGroup")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/NettyBinaryTelnetTtyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.termd.core.function.Function; 20 | import io.termd.core.function.Supplier; 21 | import io.termd.core.telnet.TelnetHandler; 22 | import io.termd.core.telnet.TelnetServerRule; 23 | 24 | import java.io.Closeable; 25 | 26 | /** 27 | * @author Julien Viet 28 | */ 29 | public class NettyBinaryTelnetTtyTest extends TelnetTtyTestBase { 30 | 31 | public NettyBinaryTelnetTtyTest() { 32 | binary = true; 33 | } 34 | 35 | @Override 36 | protected Function, Closeable> serverFactory() { 37 | return TelnetServerRule.NETTY_SERVER; 38 | } 39 | 40 | @Override 41 | protected void assertThreading(Thread connThread, Thread schedulerThread) throws Exception { 42 | assertTrue(connThread.getName().startsWith("nioEventLoopGroup")); 43 | assertTrue(schedulerThread.getName().startsWith("nioEventLoopGroup")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/examples/java/examples/events/EventsExample.java: -------------------------------------------------------------------------------- 1 | package examples.events; 2 | 3 | import io.termd.core.function.BiConsumer; 4 | import io.termd.core.function.Consumer; 5 | import io.termd.core.tty.TtyConnection; 6 | import io.termd.core.tty.TtyEvent; 7 | import io.termd.core.util.Vector; 8 | 9 | /** 10 | * This example show how to handle TTY events. 11 | */ 12 | public class EventsExample { 13 | 14 | public static void handle(final TtyConnection conn) { 15 | 16 | conn.setEventHandler(new BiConsumer() { 17 | @Override 18 | public void accept(TtyEvent event, Integer key) { 19 | switch (event) { 20 | case INTR: 21 | conn.write("You did a Ctrl-C\n"); 22 | break; 23 | case SUSP: 24 | conn.write("You did a Ctrl-Z\n"); 25 | break; 26 | case EOF: 27 | conn.write("You did a Ctrl-D: closing\n"); 28 | conn.close(); 29 | break; 30 | } 31 | } 32 | }); 33 | 34 | conn.setSizeHandler(new Consumer() { 35 | @Override 36 | public void accept(Vector size) { 37 | conn.write("You resized your terminal to " + size + "\n"); 38 | } 39 | }); 40 | 41 | conn.setTerminalTypeHandler(new Consumer() { 42 | @Override 43 | public void accept(String term) { 44 | conn.write("Your terminal is " + term + "\n"); 45 | } 46 | }); 47 | 48 | conn.setStdinHandler(new Consumer() { 49 | @Override 50 | public void accept(int[] keys) { 51 | for (int key : keys) { 52 | conn.write("You typed " + key + "\n"); 53 | } 54 | } 55 | }); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/NextHistory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * @author Julien Viet 27 | */ 28 | public class NextHistory implements Function { 29 | 30 | @Override 31 | public String name() { 32 | return "next-history"; 33 | } 34 | 35 | @Override 36 | public void apply(Readline.Interaction interaction) { 37 | List history = interaction.history(); 38 | int curr = interaction.getHistoryIndex(); 39 | if (curr >= 0) { 40 | int next = curr - 1; 41 | int[] line; 42 | if (next == -1) { 43 | line = (int[]) interaction.data().get("abc"); 44 | } else { 45 | line = history.get(next); 46 | } 47 | interaction.refresh(new LineBuffer().insert(line)); 48 | interaction.setHistoryIndex(next); 49 | } 50 | interaction.resume(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/telnet/TelnetHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.telnet; 18 | 19 | /** 20 | * The handler that defines the callbacks for a telnet connection. 21 | * 22 | * @author Julien Viet 23 | */ 24 | public class TelnetHandler { 25 | 26 | /** 27 | * The telnet connection opened. 28 | * 29 | * @param conn the connection 30 | */ 31 | protected void onOpen(TelnetConnection conn) {} 32 | 33 | /** 34 | * The telnet connection closed. 35 | */ 36 | protected void onClose() {} 37 | 38 | /** 39 | * Process data sent by the client. 40 | * 41 | * @param data the data 42 | */ 43 | protected void onData(byte[] data) {} 44 | 45 | protected void onSize(int width, int height) {} 46 | protected void onTerminalType(String terminalType) {} 47 | protected void onCommand(byte command) {} 48 | protected void onNAWS(boolean naws) {} 49 | protected void onEcho(boolean echo) {} 50 | protected void onSGA(boolean sga) {} 51 | protected void onSendBinary(boolean binary) { } 52 | protected void onReceiveBinary(boolean binary) { } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/NettyWebsocketTtyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.http.netty.NettyWebsocketTtyBootstrap; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | 25 | /** 26 | * @author Julien Viet 27 | */ 28 | public class NettyWebsocketTtyTest extends WebsocketTtyTestBase { 29 | 30 | private NettyWebsocketTtyBootstrap bootstrap; 31 | 32 | @Override 33 | protected void server(Consumer onConnect) { 34 | if (bootstrap != null) { 35 | throw failure("Server already started"); 36 | } 37 | bootstrap = new NettyWebsocketTtyBootstrap().setHost("localhost").setPort(8080); 38 | try { 39 | bootstrap.start(onConnect).get(10, TimeUnit.SECONDS); 40 | } catch (Throwable e) { 41 | throw failure(e); 42 | } 43 | } 44 | 45 | public void after() throws Exception { 46 | if (bootstrap != null) { 47 | try { 48 | bootstrap.stop().get(10, TimeUnit.SECONDS); 49 | } catch (Throwable t) { 50 | t.printStackTrace(); 51 | } 52 | bootstrap = null; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/PreviousHistory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * 27 | * @author Julien Viet 28 | */ 29 | public class PreviousHistory implements Function { 30 | 31 | @Override 32 | public String name() { 33 | return "previous-history"; 34 | } 35 | 36 | @Override 37 | public void apply(Readline.Interaction interaction) { 38 | List history = interaction.history(); 39 | if (history.size() > 0) { 40 | int curr = interaction.getHistoryIndex(); 41 | int next = curr + 1; 42 | if (next < history.size()) { 43 | if (curr == -1) { 44 | int[] tmp = interaction.buffer().toArray(); 45 | interaction.data().put("abc", tmp); 46 | } 47 | int[] line = history.get(next); 48 | interaction.refresh(new LineBuffer().insert(line)); 49 | interaction.setHistoryIndex(next); 50 | } 51 | } 52 | interaction.resume(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/util/MockProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | /** 20 | * @author Matej Lazar 21 | */ 22 | public class MockProcess { 23 | 24 | public static final String WELCOME_MESSAGE = "Hi there! I'm a long running process."; 25 | public static final String MESSAGE = "Hello again!"; 26 | public static final String FINAL_MESSAGE = "I'm done."; 27 | 28 | /** 29 | * 30 | * @param args 1: Number of repeats. 2: Delay in ms. 31 | * @throws InterruptedException 32 | */ 33 | public static void main(String[] args) throws InterruptedException { 34 | 35 | int delay = 250; 36 | int repeat = 40; 37 | 38 | if (args.length >= 1) { 39 | repeat = Integer.parseInt(args[0]); 40 | } 41 | 42 | if (args.length >= 2) { 43 | delay = Integer.parseInt(args[1]); 44 | } 45 | 46 | System.out.println(WELCOME_MESSAGE); 47 | System.out.println("I'll write to stdout test message '" + MESSAGE + "' " + repeat + " times with " + delay + "ms delay."); 48 | for (int i = 0; i < repeat; i++) { 49 | System.out.println(i + " : " + MESSAGE); 50 | Thread.sleep(delay); 51 | } 52 | System.out.println(FINAL_MESSAGE); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/ssh/TestServiceFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh; 18 | 19 | import io.termd.core.ssh.netty.NettyIoAcceptor; 20 | import io.termd.core.ssh.netty.NettyIoServiceFactory; 21 | import org.apache.sshd.common.FactoryManager; 22 | import org.apache.sshd.common.future.CloseFuture; 23 | import org.apache.sshd.common.io.IoAcceptor; 24 | import org.apache.sshd.common.io.IoHandler; 25 | import org.apache.sshd.common.io.nio2.Nio2ServiceFactory; 26 | 27 | import java.util.concurrent.ExecutorService; 28 | 29 | /** 30 | * @author Julien Viet 31 | */ 32 | public class TestServiceFactory extends Nio2ServiceFactory { 33 | 34 | private final NettyIoServiceFactory factory = new NettyIoServiceFactory(); 35 | 36 | public TestServiceFactory(FactoryManager factoryManager, ExecutorService service, boolean shutdownOnExit) { 37 | super(factoryManager, service, shutdownOnExit); 38 | } 39 | 40 | @Override 41 | public IoAcceptor createAcceptor(IoHandler handler) { 42 | return new NettyIoAcceptor(factory, handler); 43 | } 44 | 45 | @Override 46 | public CloseFuture close(boolean immediately) { 47 | factory.close(immediately); 48 | return super.close(immediately); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/util/Logging.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | /** 23 | * Encapsulate logging stuff as much as possible. 24 | * 25 | * @author Julien Viet 26 | */ 27 | public final class Logging { 28 | 29 | public static final Logger IO_ERROR = LoggerFactory.getLogger("io.termd.core.io_error"); 30 | public static final Logger READLINE = LoggerFactory.getLogger("io.termd.core.readline"); 31 | public static final Logger TERMINFO = LoggerFactory.getLogger("io.termd.core.terminfo"); 32 | 33 | /** 34 | * Log an io error reported by the IO layer that lead to closing the resource 35 | * 36 | * @param cause the error 37 | */ 38 | public static void logReportedIoError(Throwable cause) { 39 | IO_ERROR.error("Reported io error => closing", cause); 40 | } 41 | 42 | /** 43 | * Log an io error caught by Termd that could not be propagated to the call stack due 44 | * to the async nature. 45 | * 46 | * @param cause the error 47 | */ 48 | public static void logUndeclaredIoError(Throwable cause) { 49 | IO_ERROR.error("IO error", cause); 50 | } 51 | 52 | private Logging() { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/util/Wait.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | import io.termd.core.function.Supplier; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | /** 25 | * @author Matej Lazar 26 | */ 27 | public class Wait { 28 | 29 | public static void forCondition(Supplier evaluationSupplier, long timeout, TimeUnit timeUnit) throws InterruptedException, TimeoutException { 30 | forCondition(evaluationSupplier, timeout, timeUnit, ""); 31 | } 32 | 33 | public static void forCondition(Supplier evaluationSupplier, long timeout, TimeUnit timeUnit, String failedMessage) throws InterruptedException, TimeoutException { 34 | long started = System.currentTimeMillis(); 35 | while (true) { 36 | if (started + timeUnit.toMillis(timeout) < System.currentTimeMillis()) { 37 | throw new TimeoutException(failedMessage + " Reached timeout " + timeout + " " + timeUnit); 38 | } 39 | if (evaluationSupplier.get()) { 40 | break; 41 | } else { 42 | Thread.sleep(100); 43 | } 44 | } 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/NettyIoServiceFactoryFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import io.netty.channel.EventLoopGroup; 20 | import org.apache.sshd.common.FactoryManager; 21 | import org.apache.sshd.common.io.IoServiceFactory; 22 | import org.apache.sshd.common.io.IoServiceFactoryFactory; 23 | 24 | /** 25 | * @author Julien Viet 26 | */ 27 | public class NettyIoServiceFactoryFactory implements IoServiceFactoryFactory { 28 | 29 | final EventLoopGroup eventLoopGroup; 30 | final NettyIoHandlerBridge handlerBridge; 31 | 32 | public NettyIoServiceFactoryFactory() { 33 | this.eventLoopGroup = null; 34 | this.handlerBridge = new NettyIoHandlerBridge(); 35 | } 36 | 37 | public NettyIoServiceFactoryFactory(EventLoopGroup eventLoopGroup) { 38 | this.eventLoopGroup = eventLoopGroup; 39 | this.handlerBridge = new NettyIoHandlerBridge(); 40 | } 41 | 42 | public NettyIoServiceFactoryFactory(EventLoopGroup eventLoopGroup, NettyIoHandlerBridge handlerBridge) { 43 | this.eventLoopGroup = eventLoopGroup; 44 | this.handlerBridge = handlerBridge; 45 | } 46 | 47 | @Override 48 | public IoServiceFactory create(FactoryManager manager) { 49 | return new NettyIoServiceFactory(eventLoopGroup, handlerBridge); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/ForwardWord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * Move forward to the end of the next word. Words are composed of letters and digits. 25 | * 26 | * @author Julien Viet 27 | */ 28 | public class ForwardWord implements Function { 29 | 30 | @Override 31 | public String name() { 32 | return "forward-word"; 33 | } 34 | 35 | @Override 36 | public void apply(Readline.Interaction interaction) { 37 | LineBuffer buf = interaction.buffer().copy(); 38 | int size = buf.getSize(); 39 | int next; 40 | while ((next = buf.getCursor()) < size) { 41 | int codePoint = buf.getAt(next); 42 | if (Character.isLetterOrDigit(codePoint)) { 43 | break; 44 | } else { 45 | buf.moveCursor(1); 46 | } 47 | } 48 | while ((next = buf.getCursor()) < size) { 49 | int codePoint = buf.getAt(next); 50 | if (Character.isLetterOrDigit(codePoint)) { 51 | buf.moveCursor(1); 52 | } else { 53 | break; 54 | } 55 | } 56 | interaction.refresh(buf); 57 | interaction.resume(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/ssh/AsyncAuthTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.ssh; 2 | 3 | import com.jcraft.jsch.ChannelShell; 4 | import com.jcraft.jsch.JSch; 5 | import com.jcraft.jsch.JSchException; 6 | import com.jcraft.jsch.Session; 7 | import com.jcraft.jsch.UserInfo; 8 | 9 | /** 10 | * @author Julien Viet 11 | */ 12 | public class AsyncAuthTest extends AsyncAuthTestBase { 13 | 14 | protected boolean authenticate() throws Exception { 15 | 16 | JSch jsch = new JSch(); 17 | Session session; 18 | ChannelShell channel; 19 | 20 | session = jsch.getSession("whatever", "localhost", 5000); 21 | session.setPassword("whocares"); 22 | session.setUserInfo(new UserInfo() { 23 | @Override 24 | public String getPassphrase() { 25 | return null; 26 | } 27 | 28 | @Override 29 | public String getPassword() { 30 | return null; 31 | } 32 | 33 | @Override 34 | public boolean promptPassword(String s) { 35 | return false; 36 | } 37 | 38 | @Override 39 | public boolean promptPassphrase(String s) { 40 | return false; 41 | } 42 | 43 | @Override 44 | public boolean promptYesNo(String s) { 45 | return true; 46 | } // Accept all server keys 47 | 48 | @Override 49 | public void showMessage(String s) { 50 | } 51 | }); 52 | try { 53 | session.connect(); 54 | } catch (JSchException e) { 55 | if (e.getMessage().equals("Auth cancel")) { 56 | return false; 57 | } else { 58 | throw e; 59 | } 60 | } 61 | channel = (ChannelShell) session.openChannel("shell"); 62 | channel.connect(); 63 | 64 | if (channel != null) { 65 | try { channel.disconnect(); } catch (Exception ignore) {} 66 | } 67 | if (session != null) { 68 | try { session.disconnect(); } catch (Exception ignore) {} 69 | } 70 | 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/BogusPasswordAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.util.test; 20 | 21 | import org.apache.sshd.common.util.logging.AbstractLoggingBean; 22 | import org.apache.sshd.server.auth.password.PasswordAuthenticator; 23 | import org.apache.sshd.server.session.ServerSession; 24 | 25 | /** 26 | * TODO Add javadoc 27 | * 28 | * @author Apache MINA SSHD Project 29 | */ 30 | public class BogusPasswordAuthenticator extends AbstractLoggingBean implements PasswordAuthenticator { 31 | public static final BogusPasswordAuthenticator INSTANCE = new BogusPasswordAuthenticator(); 32 | 33 | public BogusPasswordAuthenticator() { 34 | super(); 35 | } 36 | 37 | @Override 38 | public boolean authenticate(String username, String password, ServerSession session) { 39 | boolean result = (username != null) && username.equals(password); 40 | if (log.isDebugEnabled()) { 41 | log.debug("authenticate({}) {} / {} - sucess = {}", session, username, password, Boolean.valueOf(result)); 42 | } 43 | 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sshd.util.test; 21 | 22 | import java.io.FilterOutputStream; 23 | import java.io.IOException; 24 | import java.io.OutputStream; 25 | 26 | /** 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class OutputCountTrackingOutputStream extends FilterOutputStream { 30 | protected long writeCount; 31 | 32 | public OutputCountTrackingOutputStream(OutputStream out) { 33 | super(out); 34 | } 35 | 36 | @Override 37 | public void write(int b) throws IOException { 38 | out.write(b); 39 | updateWriteCount(1L); 40 | } 41 | 42 | @Override 43 | public void write(byte[] b, int off, int len) throws IOException { 44 | out.write(b, off, len); // don't call super since it calls the single 'write' 45 | updateWriteCount(len); 46 | } 47 | 48 | public long getWriteCount() { 49 | return writeCount; 50 | } 51 | 52 | protected long updateWriteCount(long delta) { 53 | writeCount += delta; 54 | return writeCount; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/util/Vector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.util; 18 | 19 | /** 20 | * A two dimensional vector object, used for dimension, position, etc... 21 | * 22 | * @author Julien Viet 23 | */ 24 | public final class Vector { 25 | 26 | /** 27 | * http://softwareengineering.stackexchange.com/questions/148754/why-is-24-lines-a-common-default-terminal-height 28 | */ 29 | private static final int DEFAULT_WIDTH = 80; 30 | private static final int DEFAULT_HEIGHT = 24; 31 | 32 | private final int x; 33 | private final int y; 34 | 35 | public Vector() { 36 | this.x = DEFAULT_WIDTH; 37 | this.y = DEFAULT_HEIGHT; 38 | } 39 | 40 | public Vector(int x, int y) { 41 | this.x = x; 42 | this.y = y; 43 | } 44 | 45 | public int x() { 46 | return x; 47 | } 48 | 49 | public int y() { 50 | return y; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return x + y * 101; 56 | } 57 | 58 | @Override 59 | public boolean equals(Object obj) { 60 | if (obj == this) { 61 | return true; 62 | } 63 | if (obj instanceof Vector) { 64 | Vector that = (Vector) obj; 65 | return x == that.x && y == that.y; 66 | } 67 | return false; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "Vector[x=" + x + ",y=" + y + "]"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/ssh/AsyncAuthInteractiveTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.ssh; 2 | 3 | import com.jcraft.jsch.ChannelShell; 4 | import com.jcraft.jsch.JSch; 5 | import com.jcraft.jsch.JSchException; 6 | import com.jcraft.jsch.Session; 7 | import com.jcraft.jsch.UserInfo; 8 | 9 | /** 10 | * @author Julien Viet 11 | */ 12 | public class AsyncAuthInteractiveTest extends AsyncAuthTestBase { 13 | 14 | protected boolean authenticate() throws Exception { 15 | 16 | JSch jsch = new JSch(); 17 | Session session; 18 | ChannelShell channel; 19 | 20 | session = jsch.getSession("whatever", "localhost", 5000); 21 | session.setUserInfo(new UserInfo() { 22 | @Override 23 | public String getPassphrase() { 24 | throw new UnsupportedOperationException(); 25 | } 26 | 27 | @Override 28 | public String getPassword() { 29 | return "whocares"; 30 | } 31 | 32 | @Override 33 | public boolean promptPassword(String s) { 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean promptPassphrase(String s) { 39 | throw new UnsupportedOperationException(); 40 | } 41 | 42 | @Override 43 | public boolean promptYesNo(String s) { 44 | return true; 45 | } 46 | 47 | @Override 48 | public void showMessage(String s) { 49 | } 50 | }); 51 | try { 52 | session.connect(); 53 | } catch (JSchException e) { 54 | switch (e.getMessage()) { 55 | case "Auth cancel": 56 | case "Auth fail": 57 | return false; 58 | default: 59 | throw e; 60 | } 61 | } 62 | channel = (ChannelShell) session.openChannel("shell"); 63 | channel.connect(); 64 | 65 | if (channel != null) { 66 | try { channel.disconnect(); } catch (Exception ignore) {} 67 | } 68 | if (session != null) { 69 | try { session.disconnect(); } catch (Exception ignore) {} 70 | } 71 | 72 | return true; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/deprecated/AbstractUserAuth.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.deprecated; 20 | 21 | import org.apache.sshd.client.session.ClientSession; 22 | import org.apache.sshd.client.session.ClientSessionHolder; 23 | import org.apache.sshd.common.session.SessionHolder; 24 | import org.apache.sshd.common.util.ValidateUtils; 25 | import org.apache.sshd.common.util.logging.AbstractLoggingBean; 26 | 27 | /** 28 | */ 29 | public abstract class AbstractUserAuth 30 | extends AbstractLoggingBean 31 | implements UserAuth, SessionHolder, ClientSessionHolder { 32 | private final ClientSession session; 33 | private final String service; 34 | 35 | protected AbstractUserAuth(ClientSession session, String service) { 36 | this.session = ValidateUtils.checkNotNull(session, "No client session"); 37 | this.service = service; 38 | } 39 | 40 | @Override 41 | public ClientSession getClientSession() { 42 | return session; 43 | } 44 | 45 | @Override 46 | public final ClientSession getSession() { 47 | return getClientSession(); 48 | } 49 | 50 | public String getService() { 51 | return service; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/tty/TtyOutputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | 20 | import io.termd.core.function.Consumer; 21 | 22 | /** 23 | * @author Julien Viet 24 | */ 25 | public class TtyOutputMode implements Consumer { 26 | 27 | private final Consumer readHandler; 28 | 29 | public TtyOutputMode(Consumer readHandler) { 30 | this.readHandler = readHandler; 31 | } 32 | 33 | @Override 34 | public void accept(int[] data) { 35 | if (readHandler != null && data.length > 0) { 36 | int prev = 0; 37 | int ptr = 0; 38 | while (ptr < data.length) { 39 | // Simple implementation that works only on system that uses /n as line terminator 40 | // equivalent to 'stty onlcr' 41 | int cp = data[ptr]; 42 | if (cp == '\n') { 43 | if (ptr > prev) { 44 | sendChunk(data, prev, ptr); 45 | } 46 | readHandler.accept(new int[]{'\r','\n'}); 47 | prev = ++ptr; 48 | } else { 49 | ptr++; 50 | } 51 | } 52 | if (ptr > prev) { 53 | sendChunk(data, prev, ptr); 54 | } 55 | } 56 | } 57 | 58 | private void sendChunk(int[] data, int prev, int ptr) { 59 | int len = ptr - prev; 60 | int[] buf = new int[len]; 61 | System.arraycopy(data, prev, buf, 0, len); 62 | readHandler.accept(buf); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/TeeOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.util.test; 20 | 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | 24 | /** 25 | * TODO Add javadoc 26 | * 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class TeeOutputStream extends OutputStream { 30 | 31 | private OutputStream[] tees; 32 | 33 | public TeeOutputStream(OutputStream... tees) { 34 | this.tees = tees; 35 | } 36 | 37 | @Override 38 | public void write(int b) throws IOException { 39 | for (OutputStream s : tees) { 40 | s.write(b); 41 | } 42 | } 43 | 44 | @Override 45 | public void write(byte[] b, int off, int len) throws IOException { 46 | for (OutputStream s : tees) { 47 | s.write(b, off, len); 48 | } 49 | } 50 | 51 | @Override 52 | public void flush() throws IOException { 53 | for (OutputStream s : tees) { 54 | s.flush(); 55 | } 56 | } 57 | 58 | @Override 59 | public void close() throws IOException { 60 | for (OutputStream s : tees) { 61 | s.close(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/TtyOutputModeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.util.Helper; 21 | import org.junit.Test; 22 | 23 | 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | 27 | import static org.junit.Assert.*; 28 | 29 | /** 30 | * @author Julien Viet 31 | */ 32 | public class TtyOutputModeTest { 33 | 34 | @Test 35 | public void testTranslateLFToCRLF() { 36 | assertOutput("a", "a"); 37 | assertOutput("\r\n", "\n"); 38 | assertOutput("a\r\n", "a\n"); 39 | assertOutput("\r\na", "\na"); 40 | assertOutput("a\r\nb\r\nc", "a\nb\nc"); 41 | } 42 | 43 | private void assertOutput(String expected, String actual) { 44 | ReadHandler readHandler = new ReadHandler(); 45 | TtyOutputMode out = new TtyOutputMode(readHandler); 46 | out.accept(Helper.toCodePoints(actual)); 47 | String result = Helper.fromCodePoints(readHandler.result()); 48 | assertEquals(expected, result); 49 | } 50 | 51 | private class ReadHandler implements Consumer { 52 | List result = new LinkedList(); 53 | 54 | @Override 55 | public void accept(int[] ints) { 56 | for (int i : ints) { 57 | result.add(i); 58 | } 59 | } 60 | 61 | public int[] result() { 62 | return Helper.convert(result); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/BackwardWord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline.functions; 18 | 19 | import io.termd.core.readline.Function; 20 | import io.termd.core.readline.LineBuffer; 21 | import io.termd.core.readline.Readline; 22 | 23 | /** 24 | * Move back to the start of the current or previous word. Words are composed of letters and digits. 25 | * 26 | * @author Julien Viet 27 | */ 28 | public class BackwardWord implements Function { 29 | 30 | @Override 31 | public String name() { 32 | return "backward-word"; 33 | } 34 | 35 | @Override 36 | public void apply(Readline.Interaction interaction) { 37 | LineBuffer buf = interaction.buffer().copy(); 38 | buf.setCursor(findPos(buf)); 39 | interaction.refresh(buf); 40 | interaction.resume(); 41 | } 42 | 43 | static int findPos(LineBuffer buf) { 44 | int cursor = buf.getCursor(); 45 | int prev; 46 | while ((prev = cursor - 1) >= 0) { 47 | int codePoint = buf.getAt(prev); 48 | if (Character.isLetterOrDigit(codePoint)) { 49 | break; 50 | } else { 51 | cursor--; 52 | } 53 | } 54 | while ((prev = cursor - 1) >= 0) { 55 | int codePoint = buf.getAt(prev); 56 | if (Character.isLetterOrDigit(codePoint)) { 57 | cursor--; 58 | } else { 59 | break; 60 | } 61 | } 62 | return cursor; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/io/BinaryEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.io; 18 | 19 | import io.termd.core.function.Consumer; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.nio.CharBuffer; 23 | import java.nio.charset.Charset; 24 | import java.util.Arrays; 25 | 26 | /** 27 | * @author Julien Viet 28 | */ 29 | public class BinaryEncoder implements Consumer { 30 | 31 | private volatile Charset charset; 32 | final Consumer onByte; 33 | 34 | public BinaryEncoder(Charset charset, Consumer onByte) { 35 | this.charset = charset; 36 | this.onByte = onByte; 37 | } 38 | 39 | /** 40 | * Set a new charset on the encoder. 41 | * 42 | * @param charset the new charset 43 | */ 44 | public void setCharset(Charset charset) { 45 | this.charset = charset; 46 | } 47 | 48 | @Override 49 | public void accept(int[] codePoints) { 50 | final char[] tmp = new char[2]; 51 | int capacity = 0; 52 | for (int codePoint : codePoints) { 53 | capacity += Character.charCount(codePoint); 54 | } 55 | CharBuffer charBuf = CharBuffer.allocate(capacity); 56 | for (int codePoint : codePoints) { 57 | int size = Character.toChars(codePoint, tmp, 0); 58 | charBuf.put(tmp, 0, size); 59 | } 60 | charBuf.flip(); 61 | ByteBuffer bytesBuf = charset.encode(charBuf); 62 | byte[] bytes = bytesBuf.array(); 63 | if (bytesBuf.limit() < bytesBuf.array().length) { 64 | bytes = Arrays.copyOf(bytes, bytesBuf.limit()); 65 | } 66 | onByte.accept(bytes); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/http/websocket/server/TaskStatusUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.http.websocket.server; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.alibaba.fastjson2.annotation.JSONCreator; 5 | import com.alibaba.fastjson2.annotation.JSONField; 6 | import io.termd.core.pty.Status; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @author Matej Lazar 14 | */ 15 | public class TaskStatusUpdateEvent implements Serializable { 16 | private static final Logger log = LoggerFactory.getLogger(TaskStatusUpdateEvent.class); 17 | 18 | private final String taskId; 19 | private final Status oldStatus; 20 | private final Status newStatus; 21 | private final String context; 22 | 23 | @JSONCreator 24 | public TaskStatusUpdateEvent( 25 | @JSONField(name = "taskId") String taskId, 26 | @JSONField(name = "oldStatus") Status oldStatus, 27 | @JSONField(name = "newStatus") Status newStatus, 28 | @JSONField(name = "context") String context) { 29 | this.taskId = taskId; 30 | this.oldStatus = oldStatus; 31 | this.newStatus = newStatus; 32 | this.context = context; 33 | } 34 | 35 | public String getTaskId() { 36 | return taskId; 37 | } 38 | 39 | public Status getOldStatus() { 40 | return oldStatus; 41 | } 42 | 43 | public Status getNewStatus() { 44 | return newStatus; 45 | } 46 | 47 | public String getContext() { 48 | return context; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | try { 54 | return JSON.toJSONString(this); 55 | } catch (Exception e) { 56 | log.error("Cannot serialize object.", e); 57 | } 58 | return null; 59 | } 60 | 61 | public static TaskStatusUpdateEvent fromJson(String serialized) { 62 | try { 63 | return JSON.parseObject(serialized, TaskStatusUpdateEvent.class); 64 | } catch (Exception e) { 65 | log.error("Cannot deserialize object from json", e); 66 | return null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/telnet/netty/NettyTelnetConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.telnet.netty; 18 | 19 | import io.netty.buffer.Unpooled; 20 | import io.netty.channel.ChannelFutureListener; 21 | import io.netty.channel.ChannelHandlerContext; 22 | import io.termd.core.telnet.TelnetConnection; 23 | import io.termd.core.telnet.TelnetHandler; 24 | 25 | import java.util.concurrent.TimeUnit; 26 | 27 | /** 28 | * @author Julien Viet 29 | */ 30 | public class NettyTelnetConnection extends TelnetConnection { 31 | 32 | final ChannelHandlerContext context; 33 | 34 | public NettyTelnetConnection(TelnetHandler handler, ChannelHandlerContext context) { 35 | super(handler); 36 | this.context = context; 37 | } 38 | 39 | @Override 40 | protected void execute(Runnable task) { 41 | context.channel().eventLoop().execute(task); 42 | } 43 | 44 | @Override 45 | protected void schedule(Runnable task, long delay, TimeUnit unit) { 46 | context.channel().eventLoop().schedule(task, delay, unit); 47 | } 48 | 49 | // Not properly synchronized, but ok for now 50 | @Override 51 | protected void send(byte[] data) { 52 | context.writeAndFlush(Unpooled.buffer().writeBytes(data)); 53 | } 54 | 55 | @Override 56 | protected void onClose() { 57 | super.onClose(); 58 | } 59 | 60 | @Override 61 | public void close() { 62 | context.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 63 | } 64 | 65 | public ChannelHandlerContext channelHandlerContext() { 66 | return context; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/telnet/TelnetBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.termd.core.telnet; 17 | 18 | import io.termd.core.function.Consumer; 19 | import io.termd.core.function.Supplier; 20 | import io.termd.core.util.CompletableFuture; 21 | import io.termd.core.util.Helper; 22 | 23 | /** 24 | * A test class. 25 | * 26 | * @author Julien Viet 27 | */ 28 | public abstract class TelnetBootstrap { 29 | 30 | private String host = "localhost"; 31 | private int port = 4000; 32 | 33 | public String getHost() { 34 | return host; 35 | } 36 | 37 | public TelnetBootstrap setHost(String host) { 38 | this.host = host; 39 | return this; 40 | } 41 | 42 | public int getPort() { 43 | return port; 44 | } 45 | 46 | public TelnetBootstrap setPort(int port) { 47 | this.port = port; 48 | return this; 49 | } 50 | 51 | public CompletableFuture start(Supplier factory) { 52 | CompletableFuture fut = new CompletableFuture(); 53 | start(factory, Helper.startedHandler(fut)); 54 | return fut; 55 | } 56 | 57 | public CompletableFuture stop() { 58 | CompletableFuture fut = new CompletableFuture(); 59 | stop(Helper.stoppedHandler(fut)); 60 | return fut; 61 | } 62 | 63 | /** 64 | * Start the telnet server 65 | * 66 | * @param factory the telnet handler factory 67 | * @param doneHandler the done handler 68 | */ 69 | public abstract void start(Supplier factory, Consumer doneHandler); 70 | 71 | public abstract void stop(Consumer doneHandler); 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/SimpleUserInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.util.test; 20 | 21 | import com.jcraft.jsch.UIKeyboardInteractive; 22 | import com.jcraft.jsch.UserInfo; 23 | 24 | /** 25 | * TODO Add javadoc 26 | * 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class SimpleUserInfo implements UserInfo, UIKeyboardInteractive { 30 | 31 | private final String password; 32 | 33 | public SimpleUserInfo(String password) { 34 | this.password = password; 35 | } 36 | 37 | @Override 38 | public String getPassphrase() { 39 | return null; 40 | } 41 | 42 | @Override 43 | public String getPassword() { 44 | return password; 45 | } 46 | 47 | @Override 48 | public boolean promptPassword(String message) { 49 | return true; 50 | } 51 | 52 | @Override 53 | public boolean promptPassphrase(String message) { 54 | return false; 55 | } 56 | 57 | @Override 58 | public boolean promptYesNo(String message) { 59 | return true; 60 | } 61 | 62 | @Override 63 | public void showMessage(String message) { 64 | // ignored 65 | } 66 | 67 | @Override 68 | public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) { 69 | return new String[]{password}; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/HistorySearchForward.java: -------------------------------------------------------------------------------- 1 | 2 | package io.termd.core.readline.functions; 3 | 4 | import io.termd.core.readline.Function; 5 | import io.termd.core.readline.LineBuffer; 6 | import io.termd.core.readline.Readline; 7 | import io.termd.core.util.LineBufferUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 13 | * @author hengyunabc 2018-11-17 14 | * 15 | */ 16 | public class HistorySearchForward implements Function { 17 | 18 | @Override 19 | public String name() { 20 | return "history-search-forward"; 21 | } 22 | 23 | @Override 24 | public void apply(Readline.Interaction interaction) { 25 | LineBuffer buf = interaction.buffer().copy(); 26 | int cursor = buf.getCursor(); 27 | 28 | List history = interaction.history(); 29 | 30 | int currentHistoryIndex = interaction.getHistoryIndex(); 31 | 32 | if (currentHistoryIndex == 0 && LineBufferUtils.equals(buf, history.get(currentHistoryIndex))) { 33 | // 当前索引为0,说明上一个输入是一个空行里输入一个UP。所以重新设置为空行 34 | interaction.refresh(new LineBuffer()); 35 | interaction.setHistoryIndex(-1); 36 | } else if (currentHistoryIndex > 0 && LineBufferUtils.equals(buf, history.get(currentHistoryIndex)) 37 | && cursor == buf.getSize()) { 38 | // 当前索引大于0,并且当前行的内容和当前history index一致,说明是输入多个UP翻页得到的。所以直接返回下一条history,光标设置为行尾。 39 | interaction.refresh(new LineBuffer().insert(history.get(currentHistoryIndex - 1))); 40 | interaction.setHistoryIndex(currentHistoryIndex - 1); 41 | } else { 42 | // 找到当前光标内容 和 历史记录 里匹配的项,光标仍然设置为当前的位置 43 | int searchStart = currentHistoryIndex - 1; 44 | for (int i = searchStart; i >= 0; --i) { 45 | int[] line = history.get(i); 46 | 47 | if (LineBufferUtils.equals(buf, line)) { 48 | continue; 49 | } 50 | 51 | if (LineBufferUtils.matchBeforeCursor(buf, line)) { 52 | interaction.refresh(new LineBuffer().insert(line).setCursor(cursor)); 53 | interaction.setHistoryIndex(i); 54 | break; 55 | } 56 | } 57 | } 58 | 59 | interaction.resume(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/TermInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | import java.io.InputStream; 20 | import java.io.InputStreamReader; 21 | import java.util.Collection; 22 | import java.util.Map; 23 | 24 | /** 25 | * A term info database. 26 | * 27 | * @author Julien Viet 28 | */ 29 | public class TermInfo { 30 | 31 | private static TermInfo loadDefault() { 32 | try { 33 | InputStream in = TermInfo.class.getResourceAsStream("terminfo.src"); 34 | TermInfoParser parser = new TermInfoParser(new InputStreamReader(in, "US-ASCII")); 35 | TermInfoBuilder builder = new TermInfoBuilder(); 36 | parser.parseDatabase(builder); 37 | return builder.build(); 38 | } catch (Throwable t) { 39 | t.printStackTrace(); 40 | return null; 41 | } 42 | } 43 | 44 | private static final TermInfo DEFAULT = loadDefault(); 45 | 46 | /** 47 | * @return the default term info database loaded from the {@code terminfo.src} resource 48 | */ 49 | public static TermInfo defaultInfo() { 50 | return DEFAULT; 51 | } 52 | 53 | final Map devices; 54 | 55 | TermInfo(Map devices) { 56 | this.devices = devices; 57 | } 58 | 59 | /** 60 | * Return a particular known device given its name. 61 | * 62 | * @param name the device name 63 | * @return the device or null 64 | */ 65 | public Device getDevice(String name) { 66 | return devices.get(name); 67 | } 68 | 69 | /** 70 | * @return the devices present in this term info database. 71 | */ 72 | public Collection devices() { 73 | return devices.values(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/telnet/netty/TelnetChannelHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.telnet.netty; 18 | 19 | import io.netty.buffer.ByteBuf; 20 | import io.netty.channel.ChannelHandlerContext; 21 | import io.netty.channel.ChannelInboundHandlerAdapter; 22 | import io.termd.core.function.Supplier; 23 | import io.termd.core.telnet.TelnetHandler; 24 | import io.termd.core.util.Logging; 25 | 26 | /** 27 | * Telnet server integration with Netty {@link io.netty.channel.socket.ServerSocketChannel}. 28 | * 29 | * @author Julien Viet 30 | */ 31 | public class TelnetChannelHandler extends ChannelInboundHandlerAdapter { 32 | 33 | private final Supplier factory; 34 | private NettyTelnetConnection conn; 35 | 36 | public TelnetChannelHandler(Supplier factory) { 37 | this.factory = factory; 38 | } 39 | 40 | @Override 41 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 42 | ByteBuf buf = (ByteBuf) msg; 43 | try { 44 | int size = buf.readableBytes(); 45 | byte[] data = new byte[size]; 46 | buf.getBytes(0, data); 47 | conn.receive(data); 48 | } finally { 49 | buf.release(); 50 | } 51 | } 52 | 53 | @Override 54 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 55 | this.conn = new NettyTelnetConnection(factory.get(), ctx); 56 | conn.onInit(); 57 | } 58 | 59 | @Override 60 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 61 | conn.onClose(); 62 | this.conn = null; 63 | } 64 | 65 | @Override 66 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 67 | Logging.logReportedIoError(cause); 68 | ctx.close(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/functions/HistorySearchBackward.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline.functions; 2 | 3 | import java.util.List; 4 | 5 | import io.termd.core.readline.Function; 6 | import io.termd.core.readline.LineBuffer; 7 | import io.termd.core.readline.Readline; 8 | import io.termd.core.util.LineBufferUtils; 9 | 10 | /** 11 | * 12 | * @author hengyunabc 2018-11-16 13 | * 14 | */ 15 | public class HistorySearchBackward implements Function { 16 | 17 | @Override 18 | public String name() { 19 | return "history-search-backward"; 20 | } 21 | 22 | @Override 23 | public void apply(Readline.Interaction interaction) { 24 | LineBuffer buf = interaction.buffer().copy(); 25 | int cursor = buf.getCursor(); 26 | List history = interaction.history(); 27 | 28 | int curr = interaction.getHistoryIndex(); 29 | 30 | boolean applyNext = false; 31 | // 当前光标内容为空,则直接找前一条历史记录 32 | if (buf.getSize() == 0) { 33 | applyNext = true; 34 | } 35 | // 当前光标在行尾,并且行的内容和当前的历史记录一致,说明刚翻到当前的记录,因此直接再向前翻 36 | if (cursor == buf.getSize() && curr >= 0 && LineBufferUtils.equals(buf, history.get(curr))) { 37 | int next = curr + 1; 38 | if (next < history.size()) { 39 | applyNext = true; 40 | } 41 | } 42 | 43 | if (applyNext) { 44 | // 向前翻一条记录,光标设置为行尾 45 | int next = curr + 1; 46 | if (next < history.size()) { 47 | int[] nextHistory = history.get(next); 48 | interaction.refresh(new LineBuffer().insert(nextHistory)); 49 | interaction.setHistoryIndex(next); 50 | } 51 | } else { 52 | // 获取当前行首到光标的内容,在历史记录里查找匹配的。光标还是当前位置 53 | int searchStart = curr + 1; 54 | for (int i = searchStart; i < history.size(); ++i) { 55 | int[] line = history.get(i); 56 | if (LineBufferUtils.equals(buf, line)) { 57 | continue; 58 | } 59 | if (LineBufferUtils.matchBeforeCursor(buf, line)) { 60 | interaction.refresh(new LineBuffer().insert(line).setCursor(cursor)); 61 | interaction.setHistoryIndex(i); 62 | break; 63 | } 64 | } 65 | } 66 | 67 | interaction.resume(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/EvalContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | import io.termd.core.function.Consumer; 20 | import io.termd.core.util.Helper; 21 | 22 | import java.util.LinkedList; 23 | 24 | /** 25 | * Encapsulate evalutation state + operations. 26 | * 27 | * @author Julien Viet 28 | */ 29 | public class EvalContext { 30 | 31 | final LinkedList stack = new LinkedList(); 32 | final String[] parameters; 33 | private final Consumer result; 34 | 35 | public EvalContext(String[] parameters, Consumer result) { 36 | this.parameters = parameters; 37 | this.result = result; 38 | } 39 | 40 | public EvalContext(String[] parameters, final StringBuilder result) { 41 | this.parameters = parameters; 42 | this.result = new Consumer() { 43 | @Override 44 | public void accept(int[] codePoint) { 45 | Helper.appendCodePoints(codePoint, result); 46 | } 47 | }; 48 | } 49 | 50 | public int getParametersLength() { 51 | return parameters.length; 52 | } 53 | 54 | public String getParameter(int index) { 55 | return parameters[index]; 56 | } 57 | 58 | public void setParameter(int index, String value) { 59 | parameters[index] = value; 60 | } 61 | 62 | public String pop() { 63 | return stack.pop(); 64 | } 65 | 66 | public EvalContext push(String s) { 67 | stack.push(s); 68 | return this; 69 | } 70 | 71 | public void writeString(String s) { 72 | result.accept(Helper.toCodePoints(s)); 73 | } 74 | 75 | public void writeNumber(int number) { 76 | writeString(Integer.toString(number)); 77 | } 78 | 79 | public void writeCodePoint(int codePoint) { 80 | result.accept(new int[]{codePoint}); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/readline/HistoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | import io.termd.core.TestBase; 20 | import io.termd.core.util.Helper; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class HistoryTest extends TestBase { 27 | 28 | @Test 29 | public void testHistory() { 30 | TestTerm term = new TestTerm(this); 31 | term.readline.getHistory().add(Helper.toCodePoints("abc")); 32 | term.readline.getHistory().add(Helper.toCodePoints("def")); 33 | term.readlineComplete(); 34 | term.read(Keys.UP.sequence); 35 | term.assertScreen("% abc"); 36 | term.assertAt(0, 5); 37 | term.read(Keys.UP.sequence); 38 | term.assertScreen("% def"); 39 | term.assertAt(0, 5); 40 | term.read(Keys.UP.sequence); 41 | term.assertScreen("% def"); 42 | term.assertAt(0, 5); 43 | term.read(Keys.DOWN.sequence); 44 | term.assertScreen("% abc"); 45 | term.assertAt(0, 5); 46 | term.read(Keys.DOWN.sequence); 47 | term.assertScreen("% "); 48 | term.assertAt(0, 2); 49 | term.read(Keys.DOWN.sequence); 50 | term.assertScreen("% "); 51 | term.assertAt(0, 2); 52 | } 53 | 54 | @Test 55 | public void testMultiline() { 56 | TestTerm term = new TestTerm(this); 57 | term.readline.getHistory().add(Helper.toCodePoints("abc\ndef\nghi")); 58 | term.readlineComplete(); 59 | term.read(Keys.UP.sequence); 60 | term.assertScreen("% abc", "def", "ghi"); 61 | term.read(Keys.DOWN.sequence); 62 | term.assertScreen("% ", "", ""); 63 | } 64 | 65 | @Test 66 | public void testEmptyLineMustNotBeAddedToHistory() { 67 | TestTerm term = new TestTerm(this); 68 | term.readlineComplete(); 69 | term.read('\r'); 70 | assertEquals(0, term.readline.getHistory().size()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/tty/ReadBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.termd.core.function.Consumer; 20 | 21 | import java.util.ArrayDeque; 22 | import java.util.Queue; 23 | import java.util.concurrent.Executor; 24 | 25 | /** 26 | * @author Julien Viet 27 | */ 28 | public class ReadBuffer implements Consumer { 29 | 30 | private final Queue queue = new ArrayDeque(10); 31 | private final Executor executor; 32 | private volatile Consumer readHandler; 33 | 34 | public ReadBuffer(Executor executor) { 35 | this.executor = executor; 36 | } 37 | 38 | @Override 39 | public void accept(int[] data) { 40 | queue.add(data); 41 | while (readHandler != null && queue.size() > 0) { 42 | data = queue.poll(); 43 | if (data != null) { 44 | readHandler.accept(data); 45 | } 46 | } 47 | } 48 | 49 | public Consumer getReadHandler() { 50 | return readHandler; 51 | } 52 | 53 | public void setReadHandler(final Consumer readHandler) { 54 | if (readHandler != null) { 55 | if (this.readHandler != null) { 56 | this.readHandler = readHandler; 57 | } else { 58 | ReadBuffer.this.readHandler = readHandler; 59 | drainQueue(); 60 | } 61 | } else { 62 | this.readHandler = null; 63 | } 64 | } 65 | 66 | private void drainQueue() { 67 | if (queue.size() > 0 && readHandler != null) { 68 | executor.execute(new Runnable() { 69 | @Override 70 | public void run() { 71 | if (readHandler != null) { 72 | final int[] data = queue.poll(); 73 | if (data != null) { 74 | readHandler.accept(data); 75 | drainQueue(); 76 | } 77 | } 78 | } 79 | }); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/readline/TestTerminal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | import io.termd.core.function.Consumer; 20 | import org.junit.Assert; 21 | 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | class TestTerminal implements Consumer { 27 | 28 | final StringBuilder data = new StringBuilder(); 29 | 30 | public TestTerminal() { 31 | } 32 | 33 | @Override 34 | public void accept(int[] codePoints) { 35 | for (int c : codePoints) { 36 | data.append(Character.toChars(c)); 37 | } 38 | } 39 | 40 | public TestTerminal assertCodePoints(char... expected) { 41 | return assertCodePoints(new String(expected)); 42 | } 43 | 44 | public TestTerminal assertCodePoints(String expected) { 45 | Assert.assertEquals(readable(expected), readable(data.substring(0, Math.min(expected.length(), data.length())))); 46 | data.delete(0, expected.length()); 47 | return this; 48 | } 49 | 50 | public TestTerminal clear() { 51 | data.setLength(0); 52 | return this; 53 | } 54 | 55 | private static String readable(String s) { 56 | StringBuilder sb = new StringBuilder(); 57 | for (int i = 0;i < s.length();i++) { 58 | char c = s.charAt(i); 59 | if (c < 32) { 60 | if (c == '\n') { 61 | sb.append("\\n"); 62 | } else if (c == '\r') { 63 | sb.append("\\r"); 64 | } else if (c == '\b') { 65 | sb.append("\\b"); 66 | } else if (c == 27) { 67 | sb.append("\\033"); 68 | } else { 69 | throw new UnsupportedOperationException(); 70 | } 71 | } else { 72 | sb.append(c); 73 | } 74 | } 75 | return sb.toString(); 76 | } 77 | 78 | public TestTerminal assertEmpty() { 79 | Assert.assertEquals("", readable(data.toString())); 80 | return this; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/Sequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | import io.termd.core.function.Consumer; 20 | 21 | import java.util.Collections; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | 25 | /** 26 | * @author Julien Viet 27 | */ 28 | public class Sequence implements Iterable { 29 | 30 | private final List value; 31 | 32 | public Sequence(List value) { 33 | this.value = value; 34 | } 35 | 36 | public Sequence(String s) { 37 | value = Collections.singletonList(new OpCode.Literal(s)); 38 | } 39 | 40 | public int size() { 41 | return value.size(); 42 | } 43 | 44 | public String eval(String... parameters) { 45 | StringBuilder buffer = new StringBuilder(); 46 | eval(parameters, buffer); 47 | return buffer.toString(); 48 | } 49 | 50 | public void eval(String[] parameters, StringBuilder result) { 51 | eval(new EvalContext(parameters, result)); 52 | } 53 | 54 | public void eval(String[] parameters, Consumer result) { 55 | eval(new EvalContext(parameters, result)); 56 | } 57 | 58 | public void eval(EvalContext context) { 59 | for (OpCode op : value) { 60 | op.eval(context); 61 | } 62 | } 63 | 64 | @Override 65 | public Iterator iterator() { 66 | return value.iterator(); 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | StringBuilder buffer = new StringBuilder(); 72 | for (OpCode op : value) { 73 | op.toString(buffer); 74 | } 75 | return buffer.toString(); 76 | } 77 | 78 | @Override 79 | public boolean equals(Object obj) { 80 | if (obj == this) { 81 | return true; 82 | } 83 | if (obj instanceof Sequence) { 84 | Sequence that = (Sequence) obj; 85 | return value.equals(that.value); 86 | } 87 | return false; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/NettySshTtyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | import io.netty.channel.EventLoopGroup; 20 | import io.netty.channel.nio.NioEventLoopGroup; 21 | import io.termd.core.function.Consumer; 22 | import io.termd.core.ssh.TtyCommand; 23 | import io.termd.core.ssh.netty.NettyIoServiceFactoryFactory; 24 | import io.termd.core.ssh.netty.NettyIoSession; 25 | import org.apache.sshd.common.session.Session; 26 | import org.apache.sshd.server.SshServer; 27 | import org.junit.After; 28 | import org.junit.Before; 29 | 30 | import java.nio.charset.StandardCharsets; 31 | 32 | /** 33 | * @author Julien Viet 34 | */ 35 | public class NettySshTtyTest extends SshTtyTestBase { 36 | 37 | private EventLoopGroup eventLoopGroup; 38 | 39 | @Before 40 | public void before() { 41 | eventLoopGroup = new NioEventLoopGroup(); 42 | } 43 | 44 | @After 45 | public void after() throws Exception { 46 | eventLoopGroup.shutdownGracefully(); 47 | } 48 | 49 | @Override 50 | protected SshServer createServer() { 51 | SshServer sshd = SshServer.setUpDefaultServer(); 52 | sshd.setIoServiceFactoryFactory(new NettyIoServiceFactoryFactory(eventLoopGroup)); 53 | return sshd; 54 | } 55 | 56 | @Override 57 | protected TtyCommand createConnection(Consumer onConnect) { 58 | return new TtyCommand(charset, onConnect) { 59 | @Override 60 | public void execute(Runnable task) { 61 | Session session = this.session.getSession(); 62 | NettyIoSession ioSession = (NettyIoSession) session.getIoSession(); 63 | ioSession.execute(task); 64 | } 65 | }; 66 | } 67 | 68 | @Override 69 | protected void assertThreading(Thread connThread, Thread schedulerThread) throws Exception { 70 | assertTrue(connThread.getName().startsWith("nioEventLoopGroup")); 71 | assertEquals(connThread, schedulerThread); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sshd/util/test/JSchLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sshd.util.test; 20 | 21 | import com.jcraft.jsch.JSch; 22 | import com.jcraft.jsch.Logger; 23 | 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Apache MINA SSHD Project 28 | */ 29 | public class JSchLogger implements Logger { 30 | private final org.slf4j.Logger log = LoggerFactory.getLogger(JSch.class); 31 | 32 | public JSchLogger() { 33 | super(); 34 | } 35 | 36 | @Override 37 | public boolean isEnabled(int level) { 38 | switch (level) { 39 | case INFO: // INFO is too "chatty" so we map it to debug 40 | case DEBUG: 41 | return log.isDebugEnabled(); 42 | case WARN: 43 | return log.isWarnEnabled(); 44 | case ERROR: 45 | return log.isErrorEnabled(); 46 | case FATAL: 47 | return log.isErrorEnabled(); 48 | default: 49 | return false; 50 | } 51 | } 52 | 53 | @Override 54 | public void log(int level, String message) { 55 | switch (level) { 56 | case INFO: // INFO is too "chatty" so we map it to debug 57 | case DEBUG: 58 | log.debug(message); 59 | break; 60 | case WARN: 61 | log.warn(message); 62 | break; 63 | case ERROR: 64 | case FATAL: 65 | log.error(message); 66 | break; 67 | default: 68 | log.error("[LEVEL=" + level + "]: " + message); 69 | } 70 | } 71 | 72 | public static void init() { 73 | JSch.setLogger(new JSchLogger()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/Device.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | /** 27 | * @author Julien Viet 28 | */ 29 | public class Device { 30 | 31 | final String name; 32 | final List synonyms; 33 | final String longname; 34 | final Map, Feature> features; 35 | 36 | public Device(List names) { 37 | name = names.get(0); 38 | synonyms = names.size() > 2 ? names.subList(1, names.size() - 1) : Collections.emptyList(); 39 | longname = names.size() > 1 ? names.get(names.size() - 1) : null; 40 | features = new HashMap, Feature>(); 41 | } 42 | 43 | public Device(String name, List synonyms, String longname) { 44 | this.name = name; 45 | this.synonyms = synonyms; 46 | this.features = new HashMap, Feature>(); 47 | this.longname = longname; 48 | } 49 | 50 | public Collection> getFeatures() { 51 | return features.values(); 52 | } 53 | 54 | public T getFeature(Capability capability) { 55 | return getFeature(capability, null); 56 | } 57 | 58 | public T getFeature(Capability capability, T def) { 59 | Feature feature = features.get(capability); 60 | if (feature != null) { 61 | return capability.type.cast(feature.value); 62 | } 63 | return def; 64 | } 65 | 66 | public void addFeature(Feature feature) { 67 | features.put(feature.capability, feature); 68 | } 69 | 70 | public void addFeature(String name, Object value) { 71 | addFeature(Feature.create(name, value)); 72 | } 73 | 74 | public void addFeatures(Iterable> features) { 75 | for (Feature feature : features) { 76 | addFeature(feature); 77 | } 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "Device[name=" + name + "]"; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/io/TelnetCharset.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.io; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.CharBuffer; 21 | import java.nio.charset.Charset; 22 | import java.nio.charset.CharsetDecoder; 23 | import java.nio.charset.CharsetEncoder; 24 | import java.nio.charset.CoderResult; 25 | 26 | /** 27 | * Ascii based telnet charset. 28 | * 29 | * The decoder transforms {@code \r\n} sequence and {@code \r0} to {@code \r}. 30 | * 31 | * @author Julien Viet 32 | */ 33 | public class TelnetCharset extends Charset { 34 | 35 | public static final Charset INSTANCE = new TelnetCharset(); 36 | 37 | private TelnetCharset() { 38 | super("Telnet", new String[0]); 39 | } 40 | 41 | @Override 42 | public boolean contains(Charset cs) { 43 | return cs.name().equals(name()); 44 | } 45 | 46 | @Override 47 | public CharsetDecoder newDecoder() { 48 | return new CharsetDecoder(this, 1.0f, 1.0f) { 49 | private boolean prevCR; 50 | @Override 51 | protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 52 | int pos = in.position(); 53 | int limit = in.limit(); 54 | try { 55 | while (pos < limit) { 56 | byte b = in.get(pos); 57 | char c; 58 | if (b >= 0) { 59 | if (prevCR && (b == '\n' || b == 0)) { 60 | pos++; 61 | prevCR = false; 62 | continue; 63 | } 64 | c = (char) b; 65 | prevCR = b == '\r'; 66 | } else { 67 | c = (char)(256 + b); 68 | } 69 | if (out.position() >= out.limit()) { 70 | return CoderResult.OVERFLOW; 71 | } 72 | pos++; 73 | out.put(c); 74 | } 75 | return CoderResult.UNDERFLOW; 76 | } finally { 77 | in.position(pos); 78 | } 79 | } 80 | }; 81 | } 82 | 83 | @Override 84 | public CharsetEncoder newEncoder() { 85 | throw new UnsupportedOperationException(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/term/Feature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.term; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | public class Feature { 23 | 24 | public static Feature create(String name, T value) { 25 | Capability cap = null; 26 | if (value instanceof Boolean) { 27 | cap = (Capability) Capability.getCapability(name, Boolean.class); 28 | } else if (value instanceof Integer) { 29 | cap = (Capability) Capability.getCapability(name, Integer.class); 30 | } else if (value instanceof Sequence) { 31 | cap = (Capability) Capability.getCapability(name, Sequence.class); 32 | } 33 | if (cap == null) { 34 | cap = new Capability((Class) value.getClass(), null, name, null, null); 35 | } 36 | return new Feature(cap, value); 37 | } 38 | 39 | final Capability capability; 40 | final T value; 41 | 42 | public Feature(Capability capability, T value) { 43 | this.capability = capability; 44 | this.value = value; 45 | } 46 | 47 | public Feature(String name, T value) { 48 | this.capability = new Capability((Class) value.getClass(), name, name, null, null); 49 | this.value = value; 50 | } 51 | 52 | public T value() { 53 | return value; 54 | } 55 | 56 | public Capability capability() { 57 | return capability; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (obj == this) { 63 | return true; 64 | } 65 | if (obj instanceof Feature) { 66 | Feature that = (Feature) obj; 67 | return capability.equals(that.capability) && value.equals(that.value); 68 | } 69 | return false; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | if (value instanceof Boolean) { 75 | Boolean booleanValue = (Boolean) value; 76 | return booleanValue ? capability.name : (capability.name + "@"); 77 | } else if (value instanceof Integer) { 78 | return capability.name + "#" + value; 79 | } else { 80 | return capability.name + "=" + value; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/http/netty/TtyServerInitializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.http.netty; 18 | 19 | import io.netty.channel.ChannelInitializer; 20 | import io.netty.channel.ChannelPipeline; 21 | import io.netty.channel.group.ChannelGroup; 22 | import io.netty.channel.socket.SocketChannel; 23 | import io.netty.handler.codec.http.HttpObjectAggregator; 24 | import io.netty.handler.codec.http.HttpServerCodec; 25 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 26 | import io.netty.handler.stream.ChunkedWriteHandler; 27 | import io.termd.core.function.Consumer; 28 | import io.termd.core.tty.TtyConnection; 29 | 30 | 31 | /** 32 | * @author Julien Viet 33 | */ 34 | public class TtyServerInitializer extends ChannelInitializer { 35 | 36 | private final ChannelGroup group; 37 | private final Consumer handler; 38 | private String httpResourcePath; 39 | 40 | public TtyServerInitializer(ChannelGroup group, Consumer handler) { 41 | this(group, handler, null); 42 | } 43 | 44 | public TtyServerInitializer(ChannelGroup group, Consumer handler, String httpResourcePath) { 45 | this.group = group; 46 | this.handler = handler; 47 | this.httpResourcePath = httpResourcePath; 48 | } 49 | 50 | @Override 51 | protected void initChannel(SocketChannel ch) throws Exception { 52 | ChannelPipeline pipeline = ch.pipeline(); 53 | pipeline.addLast(new HttpServerCodec()); 54 | pipeline.addLast(new ChunkedWriteHandler()); 55 | pipeline.addLast(new HttpObjectAggregator(64 * 1024)); 56 | HttpRequestHandler httpRequestHandler = null; 57 | if (httpResourcePath == null) { 58 | httpRequestHandler = new HttpRequestHandler("/ws"); 59 | } else { 60 | httpRequestHandler = new HttpRequestHandler("/ws", httpResourcePath); 61 | } 62 | 63 | pipeline.addLast(httpRequestHandler); 64 | pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); 65 | pipeline.addLast(new TtyWebSocketFrameHandler(group, handler, HttpRequestHandler.class)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/examples/java/examples/screencast/Screencaster.java: -------------------------------------------------------------------------------- 1 | package examples.screencast; 2 | 3 | import io.termd.core.function.BiConsumer; 4 | import io.termd.core.function.Consumer; 5 | import io.termd.core.tty.TtyConnection; 6 | import io.termd.core.tty.TtyEvent; 7 | import io.termd.core.util.Vector; 8 | 9 | import java.awt.*; 10 | import java.awt.image.BufferedImage; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * Createa a screencast of the current screen to the TTY. 15 | * 16 | * This example shows how to push data to the TTY on regular intervals. 17 | */ 18 | public class Screencaster { 19 | 20 | private final Robot robot; 21 | private final TtyConnection conn; 22 | private boolean interrupted; 23 | 24 | public Screencaster(Robot robot, TtyConnection conn) { 25 | this.robot = robot; 26 | this.conn = conn; 27 | conn.setEventHandler(new BiConsumer() { 28 | @Override 29 | public void accept(TtyEvent event, Integer key) { 30 | interrupted = true; 31 | } 32 | }); 33 | } 34 | 35 | public void handle() { 36 | if (conn.size() != null) { 37 | broadcast(); 38 | } else { 39 | conn.setSizeHandler(new Consumer() { 40 | @Override 41 | public void accept(Vector size) { 42 | broadcast(); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | private void broadcast() { 49 | if (interrupted) { 50 | conn.close(); 51 | return; 52 | } 53 | BufferedImage capture = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())); 54 | Vector size = conn.size(); 55 | Image temp = capture.getScaledInstance(size.x(), size.y(), Image.SCALE_SMOOTH); 56 | BufferedImage scaled = new BufferedImage(size.x(), size.y(), BufferedImage.TYPE_INT_ARGB); 57 | Graphics2D g2d = scaled.createGraphics(); 58 | g2d.drawImage(temp, 0, 0, null); 59 | g2d.dispose(); 60 | StringBuilder sb = new StringBuilder(); 61 | for (int y = 0; y < size.y(); y++) { 62 | sb.append("\033[").append(y + 1).append(";1H"); 63 | for (int x = 0; x < size.x(); x++) { 64 | Color pixel = new Color(scaled.getRGB(x, y)); 65 | int r = pixel.getRed(); 66 | int g = pixel.getGreen(); 67 | int b = pixel.getBlue(); 68 | double grey = (r + g + b) / 3.0; 69 | if (grey < 51) { 70 | sb.append('\u2588'); 71 | } else if (grey < 102) { 72 | sb.append('\u2593'); 73 | } else if (grey < 153) { 74 | sb.append('\u2592'); 75 | } else if (grey < 204) { 76 | sb.append('\u2591'); 77 | } else { 78 | sb.append(' '); 79 | } 80 | } 81 | } 82 | conn.write(sb.toString()); 83 | conn.schedule(new Runnable() { 84 | @Override 85 | public void run() { 86 | broadcast(); 87 | } 88 | }, 100, TimeUnit.SECONDS); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/telnet/TelnetTermTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.telnet; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.function.Supplier; 5 | import io.termd.core.tty.TtyConnection; 6 | import io.termd.core.util.Vector; 7 | import org.apache.commons.net.telnet.WindowSizeOptionHandler; 8 | import org.junit.Test; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * @author Julien Viet 17 | */ 18 | public abstract class TelnetTermTest extends TelnetTestBase { 19 | 20 | @Test 21 | public void testSizeHandler() throws Exception { 22 | final CountDownLatch latch1 = new CountDownLatch(1); 23 | final CountDownLatch latch2 = new CountDownLatch(1); 24 | server.start(new Supplier() { 25 | @Override 26 | public TelnetHandler get() { 27 | final AtomicInteger count = new AtomicInteger(); 28 | return new TelnetTtyConnection(false, false, StandardCharsets.UTF_8, new Consumer() { 29 | @Override 30 | public void accept(TtyConnection conn) { 31 | conn.setSizeHandler(new Consumer() { 32 | @Override 33 | public void accept(Vector size) { 34 | switch (count.getAndIncrement()) { 35 | case 0: 36 | assertEquals(20, size.x()); 37 | assertEquals(10, size.y()); 38 | latch1.countDown(); 39 | break; 40 | case 1: 41 | assertEquals(80, size.x()); 42 | assertEquals(24, size.y()); 43 | latch2.countDown(); 44 | break; 45 | case 2: 46 | assertEquals(180, size.x()); 47 | assertEquals(160, size.y()); 48 | testComplete(); 49 | break; 50 | default: 51 | fail("Was not expecting that"); 52 | } 53 | } 54 | }); 55 | } 56 | }); 57 | } 58 | }); 59 | WindowSizeOptionHandler optionHandler = new WindowSizeOptionHandler(20, 10, false, false, true, false); 60 | client.setOptionHandler(optionHandler); 61 | client.connect("localhost", 4000); 62 | latch1.await(30, TimeUnit.SECONDS); 63 | client.writeDirectAndFlush(new byte[]{TelnetConnection.BYTE_IAC, TelnetConnection.BYTE_SB, 31, 0, 80, 0, 24, TelnetConnection.BYTE_IAC, TelnetConnection.BYTE_SE}); 64 | latch2.await(30, TimeUnit.SECONDS); 65 | client.writeDirectAndFlush(new byte[]{TelnetConnection.BYTE_IAC, TelnetConnection.BYTE_SB, 31, 0, (byte) 180, 0, (byte) 160, TelnetConnection.BYTE_IAC, TelnetConnection.BYTE_SE}); 66 | await(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/ssh/netty/NettyIoServiceFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.ssh.netty; 18 | 19 | import io.netty.channel.EventLoopGroup; 20 | import io.netty.channel.nio.NioEventLoopGroup; 21 | import io.netty.util.concurrent.Future; 22 | import io.netty.util.concurrent.GenericFutureListener; 23 | import org.apache.sshd.common.future.CloseFuture; 24 | import org.apache.sshd.common.io.IoAcceptor; 25 | import org.apache.sshd.common.io.IoConnector; 26 | import org.apache.sshd.common.io.IoHandler; 27 | import org.apache.sshd.common.io.IoServiceFactory; 28 | import org.apache.sshd.common.util.closeable.AbstractCloseable; 29 | 30 | /** 31 | * @author Julien Viet 32 | */ 33 | public class NettyIoServiceFactory extends AbstractCloseable implements IoServiceFactory { 34 | 35 | final NettyIoHandlerBridge handlerBridge; 36 | final EventLoopGroup eventLoopGroup; 37 | final boolean closeEventLoopGroup; 38 | 39 | public NettyIoServiceFactory() { 40 | this(null); 41 | } 42 | 43 | public NettyIoServiceFactory(EventLoopGroup group) { 44 | this(group, new NettyIoHandlerBridge()); 45 | } 46 | 47 | public NettyIoServiceFactory(EventLoopGroup group, NettyIoHandlerBridge handlerBridge) { 48 | this.handlerBridge = handlerBridge; 49 | this.closeEventLoopGroup = group == null; 50 | this.eventLoopGroup = group == null ? new NioEventLoopGroup() : group; 51 | } 52 | 53 | @Override 54 | public IoConnector createConnector(IoHandler handler) { 55 | throw new UnsupportedOperationException("Only implement server for now"); 56 | } 57 | 58 | @Override 59 | public IoAcceptor createAcceptor(IoHandler handler) { 60 | return new NettyIoAcceptor(this, handler); 61 | } 62 | 63 | @Override 64 | protected CloseFuture doCloseGracefully() { 65 | if (closeEventLoopGroup) { 66 | eventLoopGroup.shutdownGracefully().addListener(new GenericFutureListener>() { 67 | @Override 68 | public void operationComplete(Future future) throws Exception { 69 | closeFuture.setClosed(); 70 | } 71 | }); 72 | } else { 73 | closeFuture.setClosed(); 74 | } 75 | return closeFuture; 76 | } 77 | 78 | @Override 79 | protected void doCloseImmediately() { 80 | doCloseGracefully(); 81 | super.doCloseImmediately(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/examples/java/examples/telnet/TelnetExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package examples.telnet; 17 | 18 | import io.termd.core.function.Supplier; 19 | import io.termd.core.telnet.Option; 20 | import io.termd.core.telnet.TelnetConnection; 21 | import io.termd.core.telnet.TelnetHandler; 22 | import io.termd.core.telnet.netty.NettyTelnetBootstrap; 23 | 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * This examples shows a simple telnet server that negociates a couple of options. 28 | */ 29 | public class TelnetExample { 30 | 31 | public synchronized static void main(String[] args) throws Throwable { 32 | NettyTelnetBootstrap bootstrap = new NettyTelnetBootstrap().setHost("localhost").setPort(4000); 33 | bootstrap.start(new Supplier() { 34 | @Override 35 | public TelnetHandler get() { 36 | return new TelnetHandler() { 37 | @Override 38 | protected void onOpen(TelnetConnection conn) { 39 | System.out.println("Client connected"); 40 | 41 | // Negociate window size and terminal type 42 | conn.writeDoOption(Option.TERMINAL_TYPE); 43 | conn.writeDoOption(Option.NAWS); 44 | } 45 | 46 | @Override 47 | protected void onNAWS(boolean naws) { 48 | if (naws) { 49 | System.out.println("Client will send window size changes"); 50 | } else { 51 | System.out.println("Client won't send window size changes"); 52 | } 53 | } 54 | 55 | @Override 56 | protected void onData(byte[] data) { 57 | System.out.println("Client sent " + new String(data)); 58 | } 59 | 60 | @Override 61 | protected void onSize(int width, int height) { 62 | System.out.println("Window resized " + width + height); 63 | } 64 | 65 | @Override 66 | protected void onTerminalType(String terminalType) { 67 | System.out.println("Client declared its terminal as " + terminalType); 68 | } 69 | 70 | @Override 71 | protected void onClose() { 72 | System.out.println("Disconnected"); 73 | } 74 | }; 75 | } 76 | }).get(10, TimeUnit.SECONDS); 77 | System.out.println("Telnet server started on localhost:4000"); 78 | TelnetExample.class.wait(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/readline/QuotingTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.readline; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * @author Julien Viet 9 | */ 10 | public class QuotingTest { 11 | 12 | @Test 13 | public void testFoo() { 14 | assertEscape("a", "a"); 15 | assertEscape("\n", "\n"); 16 | assertEscape("a\n", "a\n"); 17 | assertEscape("\na", "\na"); 18 | } 19 | 20 | @Test 21 | public void testQuote() { 22 | assertEscape("'", "<'>"); 23 | assertEscape("'a", "<'>a"); 24 | assertEscape("'a'", "<'>a"); 25 | assertEscape("'\"'", "<'>\""); 26 | assertEscape("'\n'", "<'>\n"); 27 | assertEscape("'\\'", "<'>\\"); 28 | assertEscape("'a\nb'", "<'>a\nb"); 29 | assertEscape("'a'\n", "<'>a\n"); 30 | } 31 | 32 | @Test 33 | public void testDoubleQuote() { 34 | assertEscape("\"", "<\">"); 35 | assertEscape("\"a", "<\">a"); 36 | assertEscape("\"a\"", "<\">a"); 37 | assertEscape("\"'\"", "<\">'"); 38 | assertEscape("\"\n\"", "<\">\n"); 39 | assertEscape("\"\\", "<\">["); 40 | assertEscape("\"\\\"", "<\">[\"]"); 41 | assertEscape("\"\\\\", "<\">[\\]"); 42 | assertEscape("\"\\a", "<\">[a]"); 43 | assertEscape("\"a\nb\"", "<\">a\nb"); 44 | assertEscape("\"a\"\n", "<\">a\n"); 45 | } 46 | 47 | @Test 48 | public void testBackslash() { 49 | assertEscape("\\", "["); 50 | assertEscape("\\a", "[a]"); 51 | assertEscape("\\ab", "[a]b"); 52 | assertEscape("\\\\", "[\\]"); 53 | assertEscape("\\'", "[']"); 54 | assertEscape("\\\"", "[\"]"); 55 | assertEscape("\\\n", "[\n]"); 56 | } 57 | 58 | private void assertEscape(String line, String expected) { 59 | String actual = escape(line); 60 | assertEquals(expected, actual); 61 | } 62 | 63 | private String escape(String line) { 64 | final StringBuilder builder = new StringBuilder(); 65 | LineStatus.Ext buf = new LineStatus.Ext(); 66 | boolean escaping = false; 67 | int prev = 0; 68 | for (int offset = 0; offset < line.length(); ) { 69 | int cp = line.codePointAt(offset); 70 | buf.accept(cp); 71 | if (buf.isEscaping()) { 72 | builder.append("["); 73 | escaping = true; 74 | } else { 75 | if (prev != buf.getQuote()) { 76 | switch (prev) { 77 | case 0: 78 | builder.append("<").appendCodePoint(buf.getQuote()).append(">"); 79 | break; 80 | default: 81 | builder.append(""); 82 | break; 83 | } 84 | prev = buf.getQuote(); 85 | } else { 86 | builder.appendCodePoint(cp); 87 | if (escaping) { 88 | builder.append(']'); 89 | escaping = false; 90 | } 91 | } 92 | } 93 | offset += Character.charCount(cp); 94 | } 95 | return builder.toString(); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/readline/Keys.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.readline; 18 | 19 | import java.nio.IntBuffer; 20 | 21 | /** 22 | * Goal : translate a codepoint sequence into a symbol. 23 | * 24 | * @author Julien Viet 25 | */ 26 | enum Keys implements KeyEvent { 27 | 28 | CTRL_AROBASE("ctrl-@", 0), 29 | CTRL_A("Ctrl-A", 1), 30 | CTRL_B("Ctrl-B", 2), 31 | CTRL_C("Ctrl-C", 3), 32 | CTRL_D("Ctrl-D", 4), 33 | CTRL_E("Ctrl-E", 5), 34 | CTRL_F("Ctrl-F", 6), 35 | CTRL_G("Ctrl-G", 7), 36 | CTRL_H("Ctrl-H", 8), 37 | CTRL_I("Ctrl-I", 9), 38 | CTRL_J("Ctrl-J", 10), 39 | CTRL_K("Ctrl-K", 11), 40 | CTRL_L("Ctrl-L", 12), 41 | CTRL_M("Ctrl-M", 13), 42 | CTRL_N("Ctrl-N", 14), 43 | CTRL_O("Ctrl-O", 15), 44 | CTRL_P("Ctrl-P", 16), 45 | CTRL_Q("Ctrl-Q", 17), 46 | CTRL_R("Ctrl-R", 18), 47 | CTRL_S("Ctrl-S", 19), 48 | CTRL_T("Ctrl-T", 20), 49 | CTRL_U("Ctrl-U", 21), 50 | CTRL_V("Ctrl-V", 22), 51 | CTRL_W("Ctrl-W", 23), 52 | CTRL_X("Ctrl-X", 24), 53 | CTRL_Y("Ctrl-Y", 25), 54 | CTRL_Z("Ctrl-Z", 26), 55 | CTRL_LEFT_BRACE("Ctrl-[", 27), 56 | CTRL_ANTI_SLASH("Ctrl-\\", 28), // ` 57 | CTRL_RIGHT_BRACE("Ctrl-]", 29), 58 | CTRL_CARRET("Ctrl-^", 30), // ^ 59 | CTRL_UNDERSCORE("Ctrl-_", 31), 60 | 61 | A("A", 'A'), B("B", 'B'), C("C", 'C'), QUOTE("\"", '"'), 62 | BACKSLASH("\\", '\\'), 63 | 64 | UP("up", 27, '[', 'A'), 65 | DOWN("down", 27, '[', 'B'), 66 | RIGHT("right", 27, '[', 'C'), 67 | LEFT("left", 27, '[', 'D'), 68 | 69 | SHIFT_RIGHT("", 27, ']', '1', ';', '2', 'C'), 70 | SHIFT_LEFT("", 27, ']', '1', ';', '2', 'D'); 71 | 72 | final String name; 73 | final int[] sequence; 74 | 75 | Keys(String name, int... sequence) { 76 | this.name = name; 77 | this.sequence = sequence; 78 | } 79 | 80 | 81 | public int getCodePointAt(int index) { 82 | return sequence[index]; 83 | } 84 | 85 | public int length() { 86 | return sequence.length; 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "key:" + name; 92 | } 93 | 94 | @Override 95 | public IntBuffer buffer() { 96 | int length = length(); 97 | IntBuffer buf = IntBuffer.allocate(length); 98 | for (int i = 0; i < length; i++) { 99 | buf.put(getCodePointAt(i)); 100 | } 101 | buf.flip(); 102 | return buf; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/io/termd/core/tty/TtyEventDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Julien Viet 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.termd.core.tty; 18 | 19 | 20 | import io.termd.core.function.BiConsumer; 21 | import io.termd.core.function.Consumer; 22 | 23 | /** 24 | * @author Julien Viet 25 | */ 26 | public class TtyEventDecoder implements Consumer { 27 | 28 | private Consumer readHandler; 29 | private BiConsumer eventHandler; 30 | private final int vintr; 31 | private final int veof; 32 | private final int vsusp; 33 | 34 | public TtyEventDecoder(int vintr, int vsusp, int veof) { 35 | this.vintr = vintr; 36 | this.vsusp = vsusp; 37 | this.veof = veof; 38 | } 39 | 40 | public Consumer getReadHandler() { 41 | return readHandler; 42 | } 43 | 44 | public TtyEventDecoder setReadHandler(Consumer readHandler) { 45 | this.readHandler = readHandler; 46 | return this; 47 | } 48 | 49 | public BiConsumer getEventHandler() { 50 | return eventHandler; 51 | } 52 | 53 | public TtyEventDecoder setEventHandler(BiConsumer eventHandler) { 54 | this.eventHandler = eventHandler; 55 | return this; 56 | } 57 | 58 | @Override 59 | public void accept(int[] data) { 60 | if (eventHandler != null) { 61 | int index = 0; 62 | while (index < data.length) { 63 | int val = data[index]; 64 | TtyEvent event = null; 65 | if (val == vintr) { 66 | event = TtyEvent.INTR; 67 | } else if (val == vsusp) { 68 | event = TtyEvent.SUSP; 69 | } else if (val == veof) { 70 | event = TtyEvent.EOF; 71 | } 72 | if (event != null) { 73 | if (eventHandler != null) { 74 | if (readHandler != null) { 75 | int[] a = new int[index]; 76 | if (index > 0) { 77 | System.arraycopy(data, 0, a, 0, index); 78 | readHandler.accept(a); 79 | } 80 | } 81 | eventHandler.accept(event, val); 82 | int[] a = new int[data.length - index - 1]; 83 | System.arraycopy(data, index + 1, a, 0, a.length); 84 | data = a; 85 | index = 0; 86 | continue; 87 | } 88 | } 89 | index++; 90 | } 91 | } 92 | if (readHandler != null && data.length > 0) { 93 | readHandler.accept(data); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/io/TelnetCharsetTest.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.io; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.util.Helper; 5 | import org.junit.Test; 6 | 7 | import java.nio.CharBuffer; 8 | import java.nio.ByteBuffer; 9 | import java.nio.charset.CharsetDecoder; 10 | import java.util.ArrayList; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * @author Julien Viet 16 | */ 17 | public class TelnetCharsetTest { 18 | 19 | @Test 20 | public void testDecodeSingleByte() { 21 | for (int i = 13;i < 256;i++) { 22 | byte[] bytes = {(byte) i}; 23 | assertEquals("Invalid encoding at " + i, new String(new char[]{(char) i}), TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 24 | } 25 | } 26 | 27 | @Test 28 | public void testDecodeByte() { 29 | for (int i = 0;i < 256;i++) { 30 | byte[] bytes = {(byte) i, 'A'}; 31 | assertEquals("Invalid encoding at " + i, new String(new char[]{(char)i, 'A'}), TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 32 | } 33 | } 34 | 35 | @Test 36 | public void testDecodeCRLF() { 37 | for (int i = 0;i < 256;i++) { 38 | byte[] bytes = {(byte) i, '\n'}; 39 | if (i != '\r') { 40 | assertEquals("Invalid encoding at " + i, new String(new char[]{(char)i, '\n'}), TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 41 | } else { 42 | assertEquals("Invalid encoding at " + i, "\r", TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 43 | } 44 | } 45 | } 46 | 47 | @Test 48 | public void testDecodeCRNULL() { 49 | for (int i = 0;i < 256;i++) { 50 | byte[] bytes = {(byte) i, 0}; 51 | if (i != '\r') { 52 | assertEquals("Invalid encoding at " + i, new String(new char[]{(char)i, 0}), TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 53 | } else { 54 | assertEquals("Invalid encoding at " + i, "\r", TelnetCharset.INSTANCE.decode(ByteBuffer.wrap(bytes)).toString()); 55 | } 56 | } 57 | } 58 | 59 | @Test 60 | public void testDecoderOverflow() { 61 | CharsetDecoder decoder = TelnetCharset.INSTANCE.newDecoder(); 62 | assertTrue(decoder.decode(ByteBuffer.wrap(new byte[]{'A', 'B'}), CharBuffer.allocate(1), true).isOverflow()); 63 | } 64 | 65 | @Test 66 | public void testBinaryDecoder() { 67 | byte[] input = { '\n', 0, 'A'}; 68 | int[][] expectedOutput = {{'\r'},{'\r'},{'\r','A'}}; 69 | for (int i = 0;i < input.length;i++) { 70 | final ArrayList codePoints = new ArrayList(); 71 | BinaryDecoder decoder = new BinaryDecoder(512, TelnetCharset.INSTANCE, new Consumer() { 72 | @Override 73 | public void accept(int[] event) { 74 | for (int j : event) { 75 | codePoints.add(j); 76 | } 77 | } 78 | }); 79 | decoder.write(new byte[]{'\r'}); 80 | assertEquals(1, codePoints.size()); 81 | decoder.write(new byte[]{input[i]}); 82 | assertEquals(Helper.list(expectedOutput[i]), codePoints); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/io/termd/core/tty/TelnetTtyTestBase.java: -------------------------------------------------------------------------------- 1 | package io.termd.core.tty; 2 | 3 | import io.termd.core.function.Consumer; 4 | import io.termd.core.function.Function; 5 | import io.termd.core.function.Supplier; 6 | import io.termd.core.telnet.TelnetClientRule; 7 | import io.termd.core.telnet.TelnetHandler; 8 | import io.termd.core.telnet.TelnetServerRule; 9 | import io.termd.core.telnet.TelnetTtyConnection; 10 | import org.apache.commons.net.telnet.EchoOptionHandler; 11 | import org.apache.commons.net.telnet.SimpleOptionHandler; 12 | import org.apache.commons.net.telnet.TerminalTypeOptionHandler; 13 | import org.apache.commons.net.telnet.WindowSizeOptionHandler; 14 | import org.junit.Rule; 15 | 16 | import java.io.Closeable; 17 | import java.nio.charset.StandardCharsets; 18 | 19 | /** 20 | * @author Julien Viet 21 | */ 22 | public abstract class TelnetTtyTestBase extends TtyTestBase { 23 | 24 | protected boolean binary; 25 | private WindowSizeOptionHandler wsHandler; 26 | 27 | @Rule 28 | public TelnetServerRule server = new TelnetServerRule(serverFactory()); 29 | 30 | @Rule 31 | public TelnetClientRule client = new TelnetClientRule(); 32 | 33 | protected abstract Function, Closeable> serverFactory(); 34 | 35 | @Override 36 | public boolean checkDisconnected() { 37 | return client.checkDisconnected(); 38 | } 39 | 40 | protected void server(final Consumer onConnect) { 41 | server.start(new Supplier() { 42 | @Override 43 | public TelnetHandler get() { 44 | return new TelnetTtyConnection(binary, binary, charset, onConnect); 45 | } 46 | }); 47 | } 48 | 49 | @Override 50 | protected void resize(int width, int height) { 51 | } 52 | 53 | @Override 54 | protected void assertConnect(String term) throws Exception { 55 | client.setOptionHandler(new EchoOptionHandler(false, false, true, true)); 56 | if (binary) { 57 | client.setOptionHandler(new SimpleOptionHandler(0, false, false, true, true)); 58 | } 59 | if (term != null) { 60 | client.setOptionHandler(new TerminalTypeOptionHandler(term, false, false, true, false)); 61 | } 62 | client.connect("localhost", 4000); 63 | } 64 | 65 | @Override 66 | protected void assertWrite(String s) throws Exception { 67 | client.write(s.getBytes(charset)); 68 | client.flush(); 69 | } 70 | 71 | protected final void assertWriteln(String s) throws Exception { 72 | assertWrite(s + (binary ? "\r" : "\r\n")); 73 | } 74 | 75 | @Override 76 | protected String assertReadString(int len) throws Exception { 77 | return client.assertReadString(len); 78 | } 79 | 80 | @Override 81 | protected void assertDisconnect(boolean clean) throws Exception { 82 | client.disconnect(clean); 83 | } 84 | 85 | @Override 86 | public void testSize() throws Exception { 87 | wsHandler = new WindowSizeOptionHandler(80, 24, false, false, true, true); 88 | client.setOptionHandler(wsHandler); 89 | super.testSize(); 90 | } 91 | 92 | @Override 93 | public void testResize() throws Exception { 94 | // Cannot be tested with this client that does not support resize 95 | } 96 | } 97 | --------------------------------------------------------------------------------