view src/com/five_ten_sg/connectbot/transport/TN5250.java @ 29:017eeed8332c tn5250

start tn5250 integration
author Carl Byington <carl@five-ten-sg.com>
date Tue, 03 Jun 2014 16:02:29 -0700
parents cfcb8d9859a8
children d738f6b876fe
line wrap: on
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.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 android.content.Context;
import android.net.Uri;
import android.util.Log;
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 int width;
    private int height;

    private boolean connected = false;

    static final Pattern hostmask;
    static {
        hostmask = Pattern.compile("^([0-9a-z.-]+)(:(\\d+))?$", Pattern.CASE_INSENSITIVE);
    }


    public TN5250() {
       super();
   }

   public TN5250(HostBean host, TerminalBridge bridge, TerminalManager manager) {
       super(host, bridge, manager);
   }


   /**
    * @return protocol part of the URI
    */
   public static String getProtocolName() {
       return PROTOCOL;
   }

   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);
        connected = handler.connect(host.getHostname(), host.getPort());
        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. Note that the resulting failure to read
     * should call {@link TerminalBridge#dispatchDisconnect(boolean)}.  !!!
     */
    public void close() {
        handler.disconnect();
        connected = 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
    }

    public void setOptions(Map<String, String> options) {
        // do nothing
    }

    public Map<String, String> getOptions() {
        return null;
    }

    public void setCompression(boolean compression) {
        // do nothing
    }

    public void setHttpproxy(String httpproxy) {
        // do nothing
    }

    public void setUseAuthAgent(String useAuthAgent) {
        // do nothing
    }

    public void setEmulation(String emulation) {
        this.emulation = emulation;
    }

    public String getEmulation() {
        return emulation;
    }

    public void setHost(HostBean host) {
        this.host = host;
    }

    public void setBridge(TerminalBridge bridge) {
        this.bridge = bridge;
    }

    public void setManager(TerminalManager manager) {
        this.manager = manager;
    }

    /**
     * Whether or not this transport type can forward ports.
     * @return true on ability to forward ports
     */
    public boolean canForwardPorts() {
        return false;
    }

    /**
     * Adds the {@link PortForwardBean} to the list.
     * @param portForward the port forward bean to add
     * @return true on successful addition
     */
    public boolean addPortForward(PortForwardBean portForward) {
        return false;
    }

    /**
     * Enables a port forward member. After calling this method, the port forward should
     * be operational iff it could be enabled by the transport.
     * @param portForward member of our current port forwards list to enable
     * @return true on successful port forward setup
     */
    public boolean enablePortForward(PortForwardBean portForward) {
        return false;
    }

    /**
     * Disables a port forward member. After calling this method, the port forward should
     * be non-functioning iff it could be disabled by the transport.
     * @param portForward member of our current port forwards list to enable
     * @return true on successful port forward tear-down
     */
    public boolean disablePortForward(PortForwardBean portForward) {
        return false;
    }

    /**
     * Removes the {@link PortForwardBean} from the available port forwards.
     * @param portForward the port forward bean to remove
     * @return true on successful removal
     */
    public boolean removePortForward(PortForwardBean portForward) {
        return false;
    }

    /**
     * Gets a list of the {@link PortForwardBean} currently used by this transport.
     * @return the list of port forwards
     */
    public List<PortForwardBean> getPortForwards() {
        return null;
    }

    /**
     * Whether or not this transport type can transfer files.
     * @return true on ability to transfer files
     */
    public boolean canTransferFiles() {
        return false;
    }

    /**
     * Downloads the specified remote file to a local folder.
     * @param remoteFile The path to the remote file to be downloaded. Must be non-null.
     * @param localFolder The path to local folder. Null = default external storage folder.
     * @return true on success, false on failure
     */
    public boolean downloadFile(String remoteFile, String localFolder) {
        return false;
    }

    /**
     * Uploads the specified local file to the remote host.
     * @param localFile The path to the local file to be uploaded. Must be non-null.
     * @param remoteFolder The path to the remote directory. Null == default remote directory.
     * @return true on success, false on failure
     */
    public boolean uploadFile(String localFile, String remoteFile,
                              String remoteFolder, String mode) {
        return false;
    }

    @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;
    }


    @Override
    public TerminalKeyListener(TerminalManager manager, TerminalBridge bridge, vt320 buffer, String encoding) {
        return new TerminalKeyListener(manager, bridge, buffer, encoding);
    }

}