diff src/com/five_ten_sg/connectbot/transport/TN5250.java @ 79:01d939969b10

merge tn5250 branch into default
author Carl Byington <carl@five-ten-sg.com>
date Mon, 16 Jun 2014 08:24:00 -0700
parents 7ae9b0c382ec
children 8f23b05a51f7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/five_ten_sg/connectbot/transport/TN5250.java	Mon Jun 16 08:24:00 2014 -0700
@@ -0,0 +1,436 @@
+/*
+ * 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, String> mnemonics;
+
+        public vt320x5250() {
+            this(80,24);
+        }
+
+        public vt320x5250(int width, int height) {
+            super(width, height);
+            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 [] d) {
+            screen52.setField(l, c, d);
+        }
+
+        // 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 (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);
+        }
+    };
+
+
+    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";
+        handler.setSSLType(encryption);
+        screen52.setVT(handler);
+        screen52.setBuffer(buffer);
+        connected = handler.connect(host.getHostname(), host.getPort(), 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());
+    }
+
+}