line source
+ − /*
+ − * 510ConnectBot
+ − * Copyright 2014 Carl Byington
+ − *
+ − * Licensed under the Apache License, Version 2.0 (the "License");
+ − * you may not use this file except in compliance with the License.
+ − * You may obtain a copy of the License at
+ − *
+ − * http://www.apache.org/licenses/LICENSE-2.0
+ − *
+ − * Unless required by applicable law or agreed to in writing, software
+ − * distributed under the License is distributed on an "AS IS" BASIS,
+ − * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ − * See the License for the specific language governing permissions and
+ − * limitations under the License.
+ − */
+ −
+ − package com.five_ten_sg.connectbot.transport;
+ −
+ − import java.io.IOException;
+ − import java.io.InputStream;
+ − import java.io.OutputStream;
+ − import java.net.Socket;
+ − import java.net.SocketException;
+ − import java.net.UnknownHostException;
+ − import java.util.HashMap;
+ − import java.util.List;
+ − import java.util.Map;
+ − import java.util.regex.Matcher;
+ − import java.util.regex.Pattern;
+ −
+ − import org.tn5250j.framework.tn5250.Screen5250;
+ − import org.tn5250j.framework.tn5250.tnvt;
+ −
+ − import com.five_ten_sg.connectbot.R;
+ − import com.five_ten_sg.connectbot.bean.HostBean;
+ − import com.five_ten_sg.connectbot.bean.PortForwardBean;
+ − import com.five_ten_sg.connectbot.service.TerminalBridge;
+ − import com.five_ten_sg.connectbot.service.TerminalKeyListener;
+ − import com.five_ten_sg.connectbot.service.TerminalManager;
+ − import com.five_ten_sg.connectbot.util.HostDatabase;
+ − import com.five_ten_sg.connectbot.util.PreferenceConstants;
+ − import android.content.Context;
+ − import android.net.Uri;
+ − import android.util.Log;
+ − import android.view.KeyCharacterMap;
+ − import android.view.KeyEvent;
+ − import android.view.View;
+ − import de.mud.terminal.vt320;
+ −
+ −
+ − /**
+ − * @author Carl Byington
+ − *
+ − */
+ − public class TN5250 extends AbsTransport {
+ − private static final String PROTOCOL = "tn5250";
+ − private static final String TAG = "ConnectBot.tn5250";
+ − private static final int DEFAULT_PORT = 23;
+ −
+ − private Screen5250 screen52;
+ − private tnvt handler = null;
+ − private Socket socket;
+ − private boolean connected = false;
+ −
+ − static final Pattern hostmask;
+ − static {
+ − hostmask = Pattern.compile("^([0-9a-z.-]+)(:(\\d+))?$", Pattern.CASE_INSENSITIVE);
+ − }
+ −
+ −
+ − class vt320x5250 extends vt320 {
+ − private HashMap<Integer, Integer> controls;
+ − private HashMap<Integer, String> mnemonics;
+ −
+ − public vt320x5250() {
+ − this(80, 24);
+ − }
+ −
+ − public vt320x5250(int width, int height) {
+ − super(width, height);
+ − controls = new HashMap<Integer, Integer>();
+ − controls.put(0x08, KEY_BACK_SPACE);
+ − controls.put(0x09, KEY_TAB);
+ − controls.put(0x0d, KEY_ENTER);
+ − controls.put(0x1b, KEY_ESCAPE);
+ − mnemonics = new HashMap<Integer, String>();
+ − mnemonics.put(KEY_PAUSE , "[attn]");
+ − mnemonics.put(KEY_F1 , "[pf1]");
+ − mnemonics.put(KEY_F2 , "[pf2]");
+ − mnemonics.put(KEY_F3 , "[pf3]");
+ − mnemonics.put(KEY_F4 , "[pf4]");
+ − mnemonics.put(KEY_F5 , "[pf5]");
+ − mnemonics.put(KEY_F6 , "[pf6]");
+ − mnemonics.put(KEY_F7 , "[pf7]");
+ − mnemonics.put(KEY_F8 , "[pf8]");
+ − mnemonics.put(KEY_F9 , "[pf9]");
+ − mnemonics.put(KEY_F10 , "[pf10]");
+ − mnemonics.put(KEY_F11 , "[pf11]");
+ − mnemonics.put(KEY_F12 , "[pf12]");
+ − mnemonics.put(KEY_UP , "[up]");
+ − mnemonics.put(KEY_DOWN , "[down]");
+ − mnemonics.put(KEY_LEFT , "[left]");
+ − mnemonics.put(KEY_RIGHT , "[right]");
+ − mnemonics.put(KEY_PAGE_DOWN , "[pgdown]");
+ − mnemonics.put(KEY_PAGE_UP , "[pgup]");
+ − mnemonics.put(KEY_INSERT , "[insert]");
+ − mnemonics.put(KEY_DELETE , "[delete]");
+ − mnemonics.put(KEY_BACK_SPACE , "[backspace]");
+ − mnemonics.put(KEY_HOME , "[home]");
+ − mnemonics.put(KEY_END , "[end]");
+ − mnemonics.put(KEY_NUM_LOCK , "");
+ − mnemonics.put(KEY_CAPS_LOCK , "");
+ − mnemonics.put(KEY_SHIFT , "");
+ − mnemonics.put(KEY_CONTROL , "");
+ − mnemonics.put(KEY_ALT , "");
+ − mnemonics.put(KEY_ENTER , "[enter]");
+ − mnemonics.put(KEY_NUMPAD0 , "0");
+ − mnemonics.put(KEY_NUMPAD1 , "1");
+ − mnemonics.put(KEY_NUMPAD2 , "2");
+ − mnemonics.put(KEY_NUMPAD3 , "3");
+ − mnemonics.put(KEY_NUMPAD4 , "4");
+ − mnemonics.put(KEY_NUMPAD5 , "5");
+ − mnemonics.put(KEY_NUMPAD6 , "6");
+ − mnemonics.put(KEY_NUMPAD7 , "7");
+ − mnemonics.put(KEY_NUMPAD8 , "8");
+ − mnemonics.put(KEY_NUMPAD9 , "9");
+ − mnemonics.put(KEY_DECIMAL , ".");
+ − mnemonics.put(KEY_ADD , "+");
+ − mnemonics.put(KEY_ESCAPE , "");
+ − mnemonics.put(KEY_TAB , "[tab]");
+ − }
+ −
+ − @Override
+ − public void debug(String s) {
+ − Log.d(TAG, s);
+ − }
+ −
+ − // monitor injecting a field
+ − @Override
+ − public void setField(int l, int c, char [] data) {
+ − screen52.setField(l, c, data);
+ − }
+ −
+ − // terminal key listener sending to local screen
+ − @Override
+ − public void write(byte[] b) {
+ − if (bridge.monitor != null) bridge.monitor.hostData(b);
+ −
+ − screen52.sendKeys(new String(b));
+ − }
+ − @Override
+ − public void write(int b) {
+ − if (controls.containsKey(b)) {
+ − keyPressed(controls.get(b), ' ', 0);
+ − }
+ − else {
+ − if (bridge.monitor != null) bridge.monitor.hostData(b);
+ −
+ − screen52.sendKeys(new String(new byte[] {(byte)b}));
+ − }
+ − }
+ − @Override
+ − public void keyPressed(int keyCode, char keyChar, int modifiers) {
+ − if (mnemonics.containsKey(keyCode)) {
+ − String s = mnemonics.get(keyCode);
+ −
+ − if (s != "") {
+ − if (bridge.monitor != null) bridge.monitor.hostData(s.getBytes());
+ −
+ − screen52.sendKeys(s);
+ − }
+ − }
+ − }
+ − // 5250 writing to the screen
+ − // test for changed screen contents
+ − @Override
+ − public void testChanged() {
+ − if (bridge.monitor != null) bridge.monitor.testChanged();
+ − }
+ − @Override
+ − public void putChar(int c, int l, char ch, int attributes) {
+ − if (bridge.monitor != null) bridge.monitor.screenChanged(l, c);
+ −
+ − super.putChar(c, l, ch, attributes);
+ − }
+ − @Override
+ − public void setCursorPosition(int c, int l) {
+ − if (bridge.monitor != null) bridge.monitor.cursorMove(l, c);
+ −
+ − super.setCursorPosition(c, l);
+ − redraw();
+ − }
+ − };
+ −
+ −
+ − public TN5250() {
+ − super();
+ − }
+ −
+ −
+ − /**
+ − * @return protocol part of the URI
+ − */
+ − public static String getProtocolName() {
+ − return PROTOCOL;
+ − }
+ −
+ −
+ − /**
+ − * Encode the current transport into a URI that can be passed via intent calls.
+ − * @return URI to host
+ − */
+ − public Uri getUri(String input) {
+ − Matcher matcher = hostmask.matcher(input);
+ −
+ − if (!matcher.matches())
+ − return null;
+ −
+ − StringBuilder sb = new StringBuilder();
+ − sb.append(PROTOCOL)
+ − .append("://")
+ − .append(matcher.group(1));
+ − String portString = matcher.group(3);
+ − int port = DEFAULT_PORT;
+ −
+ − if (portString != null) {
+ − try {
+ − port = Integer.parseInt(portString);
+ −
+ − if (port < 1 || port > 65535) {
+ − port = DEFAULT_PORT;
+ − }
+ − }
+ − catch (NumberFormatException nfe) {
+ − // Keep the default port
+ − }
+ − }
+ −
+ − if (port != DEFAULT_PORT) {
+ − sb.append(':');
+ − sb.append(port);
+ − }
+ −
+ − sb.append("/#")
+ − .append(Uri.encode(input));
+ − Uri uri = Uri.parse(sb.toString());
+ − return uri;
+ − }
+ −
+ −
+ − /**
+ − * Causes transport to connect to the target host. After connecting but before a
+ − * session is started, must call back to {@link TerminalBridge#onConnected()}.
+ − * After that call a session may be opened.
+ − */
+ − @Override
+ − public void connect() {
+ − screen52 = new Screen5250();
+ − handler = new tnvt(screen52, true, false, bridge, manager);
+ − String encryption = host.getEncryption5250();
+ −
+ − if ((encryption == null) || (encryption.length() == 0)) encryption = "NONE";
+ −
+ − screen52.setVT(handler);
+ − screen52.setBuffer(buffer);
+ − connected = handler.connect(host.getHostname(), host.getPort(), encryption, homeDirectory, buffer);
+ −
+ − if (connected) bridge.onConnected();
+ − }
+ −
+ −
+ − /**
+ − * Checks if read() will block. If there are no bytes remaining in
+ − * the underlying transport, return true.
+ − */
+ − @Override
+ − public boolean willBlock() {
+ − // we don't use a relay thread between the transport and the vt320 buffer
+ − return true;
+ − }
+ −
+ −
+ − /**
+ − * Reads from the transport. Transport must support reading into a byte array
+ − * <code>buffer</code> at the start of <code>offset</code> and a maximum of
+ − * <code>length</code> bytes. If the remote host disconnects, throw an
+ − * {@link IOException}.
+ − * @param buffer byte buffer to store read bytes into
+ − * @param offset where to start writing in the buffer
+ − * @param length maximum number of bytes to read
+ − * @return number of bytes read
+ − * @throws IOException when remote host disconnects
+ − */
+ − public int read(byte[] buffer, int offset, int length) throws IOException {
+ − // we don't use a relay thread between the transport and the vt320 buffer
+ − return 0;
+ − }
+ −
+ −
+ − /**
+ − * Writes to the transport. If the host is not yet connected, simply return without
+ − * doing anything. An {@link IOException} should be thrown if there is an error after
+ − * connection.
+ − * @param buffer bytes to write to transport
+ − * @throws IOException when there is a problem writing after connection
+ − */
+ − public void write(byte[] buffer) throws IOException {
+ − }
+ −
+ −
+ − /**
+ − * Writes to the transport. See {@link #write(byte[])} for behavior details.
+ − * @param c character to write to the transport
+ − * @throws IOException when there is a problem writing after connection
+ − */
+ − public void write(int c) throws IOException {
+ − }
+ −
+ −
+ − /**
+ − * Flushes the write commands to the transport.
+ − * @throws IOException when there is a problem writing after connection
+ − */
+ − public void flush() throws IOException {
+ − }
+ −
+ −
+ − /**
+ − * Closes the connection to the terminal.
+ − */
+ − public void close() {
+ − handler.disconnect();
+ − connected = false;
+ − bridge.dispatchDisconnect(false);
+ − }
+ −
+ −
+ − /**
+ − * Tells the transport what dimensions the display is currently
+ − * @param columns columns of text
+ − * @param rows rows of text
+ − * @param width width in pixels
+ − * @param height height in pixels
+ − */
+ − @Override
+ − public void setDimensions(int columns, int rows, int width, int height) {
+ − // do nothing
+ − }
+ −
+ −
+ − @Override
+ − public vt320 getTransportBuffer() {
+ − buffer = new vt320x5250();
+ − return setupTransportBuffer();
+ − }
+ −
+ −
+ − @Override
+ − public int getDefaultPort() {
+ − return DEFAULT_PORT;
+ − }
+ −
+ −
+ − @Override
+ − public boolean isConnected() {
+ − return connected;
+ − }
+ −
+ −
+ − @Override
+ − public boolean isSessionOpen() {
+ − return connected;
+ − }
+ −
+ −
+ − @Override
+ − public boolean isAuthenticated() {
+ − return connected;
+ − }
+ −
+ −
+ − @Override
+ − public String getDefaultNickname(String username, String hostname, int port) {
+ − if (port == DEFAULT_PORT) {
+ − return String.format("%s", hostname);
+ − }
+ − else {
+ − return String.format("%s:%d", hostname, port);
+ − }
+ − }
+ −
+ −
+ − @Override
+ − public void getSelectionArgs(Uri uri, Map<String, String> selection) {
+ − selection.put(HostDatabase.FIELD_HOST_PROTOCOL, PROTOCOL);
+ − selection.put(HostDatabase.FIELD_HOST_NICKNAME, uri.getFragment());
+ − selection.put(HostDatabase.FIELD_HOST_HOSTNAME, uri.getHost());
+ − int port = uri.getPort();
+ −
+ − if (port < 0)
+ − port = DEFAULT_PORT;
+ −
+ − selection.put(HostDatabase.FIELD_HOST_PORT, Integer.toString(port));
+ − }
+ −
+ −
+ − @Override
+ − public HostBean createHost(Uri uri) {
+ − HostBean host = new HostBean();
+ − host.setProtocol(PROTOCOL);
+ − host.setHostname(uri.getHost());
+ − int port = uri.getPort();
+ −
+ − if (port < 0)
+ − port = DEFAULT_PORT;
+ −
+ − host.setPort(port);
+ − String nickname = uri.getFragment();
+ −
+ − if (nickname == null || nickname.length() == 0) {
+ − host.setNickname(getDefaultNickname(host.getUsername(),
+ − host.getHostname(), host.getPort()));
+ − }
+ − else {
+ − host.setNickname(uri.getFragment());
+ − }
+ −
+ − return host;
+ − }
+ −
+ −
+ − public String getFormatHint(Context context) {
+ − return String.format("%s:%s",
+ − context.getString(R.string.format_hostname),
+ − context.getString(R.string.format_port));
+ − }
+ −
+ −
+ − @Override
+ − public boolean usesNetwork() {
+ − return true;
+ − }
+ −
+ −
+ − @Override
+ − public boolean needsRelay() {
+ − // we don't use a relay thread between the transport and the vt320 buffer
+ − return false;
+ − }
+ −
+ − public TerminalKeyListener getTerminalKeyListener() {
+ − return new TerminalKeyListener(manager, bridge, buffer, host.getEncoding());
+ − }
+ −
+ − }