Mercurial > 510Connectbot
view app/src/main/java/org/tn5250j/framework/tn5250/tnvt.java @ 477:d7461da8145b stable-1.9.3-9
only bind to the MonitorService for localhost monitor connections
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Sun, 20 Oct 2019 14:47:27 -0700 |
parents | 5ce5235adde6 |
children |
line wrap: on
line source
/** * Title: tnvt.java * Copyright: Copyright (c) 2001 Company: * * @author Kenneth J. Pouncey * @version 0.5 * * Description: * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this software; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package org.tn5250j.framework.tn5250; import static java.lang.Math.max; import static java.lang.Math.min; import static org.tn5250j.TN5250jConstants.AID_HELP; import static org.tn5250j.TN5250jConstants.AID_PRINT; import static org.tn5250j.TN5250jConstants.CMD_CLEAR_FORMAT_TABLE; import static org.tn5250j.TN5250jConstants.CMD_CLEAR_UNIT; import static org.tn5250j.TN5250jConstants.CMD_CLEAR_UNIT_ALTERNATE; import static org.tn5250j.TN5250jConstants.CMD_READ_INPUT_FIELDS; import static org.tn5250j.TN5250jConstants.CMD_READ_MDT_FIELDS; import static org.tn5250j.TN5250jConstants.CMD_READ_MDT_IMMEDIATE_ALT; import static org.tn5250j.TN5250jConstants.CMD_READ_SCREEN_IMMEDIATE; import static org.tn5250j.TN5250jConstants.CMD_READ_SCREEN_TO_PRINT; import static org.tn5250j.TN5250jConstants.CMD_RESTORE_SCREEN; import static org.tn5250j.TN5250jConstants.CMD_ROLL; import static org.tn5250j.TN5250jConstants.CMD_SAVE_SCREEN; import static org.tn5250j.TN5250jConstants.CMD_WRITE_ERROR_CODE; import static org.tn5250j.TN5250jConstants.CMD_WRITE_ERROR_CODE_TO_WINDOW; import static org.tn5250j.TN5250jConstants.CMD_WRITE_STRUCTURED_FIELD; import static org.tn5250j.TN5250jConstants.CMD_WRITE_TO_DISPLAY; import static org.tn5250j.TN5250jConstants.NR_REQUEST_ERROR; import static org.tn5250j.TN5250jConstants.PF1; import static org.tn5250j.TN5250jConstants.PF10; import static org.tn5250j.TN5250jConstants.PF11; import static org.tn5250j.TN5250jConstants.PF12; import static org.tn5250j.TN5250jConstants.PF13; import static org.tn5250j.TN5250jConstants.PF14; import static org.tn5250j.TN5250jConstants.PF15; import static org.tn5250j.TN5250jConstants.PF16; import static org.tn5250j.TN5250jConstants.PF17; import static org.tn5250j.TN5250jConstants.PF18; import static org.tn5250j.TN5250jConstants.PF19; import static org.tn5250j.TN5250jConstants.PF2; import static org.tn5250j.TN5250jConstants.PF20; import static org.tn5250j.TN5250jConstants.PF21; import static org.tn5250j.TN5250jConstants.PF22; import static org.tn5250j.TN5250jConstants.PF23; import static org.tn5250j.TN5250jConstants.PF24; import static org.tn5250j.TN5250jConstants.PF3; import static org.tn5250j.TN5250jConstants.PF4; import static org.tn5250j.TN5250jConstants.PF5; import static org.tn5250j.TN5250jConstants.PF6; import static org.tn5250j.TN5250jConstants.PF7; import static org.tn5250j.TN5250jConstants.PF8; import static org.tn5250j.TN5250jConstants.PF9; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Arrays; import java.util.Properties; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLSocket; import android.content.Intent; import android.net.Uri; import android.util.Log; import com.five_ten_sg.connectbot.R; import com.five_ten_sg.connectbot.bean.HostBean; import com.five_ten_sg.connectbot.service.TerminalBridge; import com.five_ten_sg.connectbot.service.TerminalManager; import de.mud.terminal.vt320; import org.tn5250j.TN5250jConstants; import org.tn5250j.encoding.CharMappings; import org.tn5250j.encoding.ICodePage; import org.tn5250j.framework.transport.SocketConnector; public final class tnvt implements Runnable { private static final String TAG = "tnvt"; // negotiating commands private static final byte IAC = (byte) - 1; // 255 FF private static final byte DONT = (byte) - 2; //254 FE private static final byte DO = (byte) - 3; //253 FD private static final byte WONT = (byte) - 4; //252 FC private static final byte WILL = (byte) - 5; //251 FB private static final byte SB = (byte) - 6; //250 Sub Begin FA private static final byte SE = (byte) - 16; //240 Sub End F0 private static final byte EOR = (byte) - 17; //239 End of Record EF private static final byte TERMINAL_TYPE = (byte) 24; // 18 private static final byte OPT_END_OF_RECORD = (byte) 25; // 19 private static final byte TRANSMIT_BINARY = (byte) 0; // 0 private static final byte QUAL_IS = (byte) 0; // 0 private static final byte TIMING_MARK = (byte) 6; // 6 private static final byte NEW_ENVIRONMENT = (byte) 39; // 27 private static final byte IS = (byte) 0; // 0 private static final byte SEND = (byte) 1; // 1 private static final byte INFO = (byte) 2; // 2 private static final byte VAR = (byte) 0; // 0 private static final byte VALUE = (byte) 1; // 1 private static final byte NEGOTIATE_ESC = (byte) 2; // 2 private static final byte USERVAR = (byte) 3; // 3 // miscellaneous private static final byte ESC = 0x04; // 04 private Socket sock; public BufferedInputStream bin; public BufferedOutputStream bout; private final BlockingQueue<Object> dsq = new ArrayBlockingQueue<Object>(25); private Stream5250 bk; private DataStreamProducer producer; protected Screen5250 screen52; private boolean waitingForInput; private boolean invited; private boolean negotiated = false; private Thread me; private Thread pthread; private int readType; private boolean enhanced = true; private boolean cursorOn = false; private String hostname = ""; private int port = 23; private vt320 buffer; private boolean connected = false; private boolean support132 = true; private ByteArrayOutputStream baosp = null; private ByteArrayOutputStream baosrsp = null; private int devSeq = -1; private String devName; private String devNameUsed; private KbdTypesCodePages kbdTypesCodePage; // WVL - LDC : TR.000300 : Callback scenario from 5250 private boolean scan; // = false; private static int STRSCAN = 1; // WVL - LDC : 05/08/2005 : TFX.006253 - support STRPCCMD private boolean strpccmd; // = false; private String encryption; private String user; private String password = null; private String library; private String initialMenu; private String program; private boolean keepTrucking = true; private boolean pendingUnlock = false; private boolean[] dataIncluded; protected ICodePage codePage; private String sslType; private WTDSFParser sfParser; private TerminalBridge bridge; private TerminalManager manager; /** * @param screen52 * @param enhanced * @param support132 * @param bridge * @param manager */ public tnvt(Screen5250 screen52, boolean enhanced, boolean support132, TerminalBridge bridge, TerminalManager manager) { this.screen52 = screen52; this.support132 = support132; this.enhanced = enhanced; this.bridge = bridge; this.manager = manager; setCodePage("37"); dataIncluded = new boolean[24]; baosp = new ByteArrayOutputStream(); baosrsp = new ByteArrayOutputStream(); } public void showURL(String url) { if (url.indexOf("://") < 0) url = "http://" + url; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); manager.startActivity(intent); } public String getHostName() { return hostname; } public void setDeviceName(String name) { devName = name; } public String getDeviceName() { return devName; } public String getAllocatedDeviceName() { return devNameUsed; } public boolean isConnected() { return connected; } /** * @return true when SSL is used and socket is connected. * @see {@link #isConnected()} */ public boolean isSslSocket() { if (this.connected && this.sock != null && this.sock instanceof SSLSocket) { return true; } else { return false; } } public final void setProxy(String proxyHost, String proxyPort) { Properties systemProperties = System.getProperties(); systemProperties.put("socksProxySet", "true"); systemProperties.put("socksProxyHost", proxyHost); systemProperties.put("socksProxyPort", proxyPort); System.setProperties(systemProperties); Log.i(TAG, " socks set "); } private String fixer(String value, String def) { if ((value == null) || (value.length() == 0)) return def; return value; } public final boolean connect(HostBean host, String homeDirectory, vt320 buffer) { try { this.hostname = host.getHostname(); this.port = host.getPort(); this.buffer = buffer; this.encryption = fixer(host.getEncryption5250(), "NONE"); this.user = host.getUsername(); this.library = host.getLibrary(); this.initialMenu = host.getInitialMenu(); this.program = host.getProgram(); try { screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED, "X - Connecting"); } catch (Exception exc) { Log.w(TAG, "setStatus(ON) " + exc.getMessage()); } SocketConnector sc = new SocketConnector(); sock = sc.createSocket(hostname, port, encryption, homeDirectory, bridge, manager); if (sock == null) { Log.w(TAG, "I did not get a socket"); disconnect(); return false; } connected = true; // used for JDK1.3 sock.setKeepAlive(true); sock.setTcpNoDelay(true); sock.setSoLinger(false, 0); InputStream in = sock.getInputStream(); OutputStream out = sock.getOutputStream(); bin = new BufferedInputStream(in, 8192); bout = new BufferedOutputStream(out); byte abyte0[]; while (negotiate(abyte0 = readNegotiations())); negotiated = true; try { screen52.setCursorActive(false); } catch (Exception excc) { Log.w(TAG, "setCursorOff " + excc.getMessage()); } producer = new DataStreamProducer(this, bin, dsq, abyte0); pthread = new Thread(producer); pthread.setPriority(Thread.NORM_PRIORITY); pthread.start(); try { screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_NOTINHIBITED, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); } catch (Exception exc) { Log.w(TAG, "setStatus(OFF) " + exc.getMessage()); } keepTrucking = true; me = new Thread(this); me.start(); } catch (Exception exception) { if (exception.getMessage() == null) exception.printStackTrace(); Log.w(TAG, "connect() " + exception.getMessage()); if (sock == null) Log.w(TAG, "I did not get a socket"); disconnect(); return false; } return true; } public final boolean disconnect() { if (!connected) { screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED, "X - Disconnected"); return false; } if (me != null && me.isAlive()) { me.interrupt(); keepTrucking = false; pthread.interrupt(); } screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED, "X - Disconnected"); screen52.getOIA().setKeyBoardLocked(false); pendingUnlock = false; try { if (sock != null) { Log.i(TAG, "Closing socket"); sock.close(); } if (bin != null) bin.close(); if (bout != null) bout.close(); connected = false; // WVL - LDC : TR.000345 : properly disconnect and clear screen // Is this the right place to set screen realestate on disconnect? //controller.getScreen().clearAll(); screen52.goto_XY(0); screen52.setCursorActive(false); screen52.clearAll(); screen52.restoreScreen(); } catch (Exception exception) { Log.w(TAG, exception.getMessage()); connected = false; devSeq = -1; return false; } devSeq = -1; return true; } private final ByteArrayOutputStream appendByteStream(byte abyte0[]) { ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(); for (int i = 0; i < abyte0.length; i++) { bytearrayoutputstream.write(abyte0[i]); if (abyte0[i] == -1) bytearrayoutputstream.write(-1); } return bytearrayoutputstream; } private final byte[] readNegotiations() throws IOException { int i = bin.read(); if (i < 0) { throw new IOException("Connection closed."); } else { int j = bin.available(); byte abyte0[] = new byte[j + 1]; abyte0[0] = (byte) i; bin.read(abyte0, 1, j); return abyte0; } } private final void writeByte(byte abyte0[]) throws IOException { bout.write(abyte0); bout.flush(); } private final void readImmediate(int readType) { if (screen52.isStatusErrorCode()) { screen52.restoreErrorLine(); screen52.setStatus(Screen5250.STATUS_ERROR_CODE, Screen5250.STATUS_VALUE_OFF, null); } if (!enhanced) { screen52.setCursorActive(false); } // screen52.setStatus(Screen5250.STATUS_SYSTEM, // Screen5250.STATUS_VALUE_ON, null); screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); screen52.getOIA().setKeyBoardLocked(true); pendingUnlock = false; invited = false; screen52.getScreenFields().readFormatTable(baosp, readType, codePage); try { Log.i(TAG, "readImmediate() writeGDS()"); writeGDS(0, 3, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } baosp.reset(); } public final boolean sendAidKey(int aid) { if (screen52.isStatusErrorCode()) { screen52.restoreErrorLine(); screen52.setStatus(Screen5250.STATUS_ERROR_CODE, Screen5250.STATUS_VALUE_OFF, null); } if (!enhanced) { screen52.setCursorActive(false); } // screen52.setStatus(Screen5250.STATUS_SYSTEM, // Screen5250.STATUS_VALUE_ON, null); screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); screen52.getOIA().setKeyBoardLocked(true); pendingUnlock = false; invited = false; baosp.write(screen52.getCurrentRow()); baosp.write(screen52.getCurrentCol()); baosp.write(aid); if (dataIncluded(aid)) screen52.getScreenFields().readFormatTable(baosp, readType, codePage); try { Log.i(TAG, "sendAidKey() writeGDS()"); writeGDS(0, 3, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); baosp.reset(); return false; } baosp.reset(); return true; } private boolean dataIncluded(int aid) { switch (aid) { case PF1: return !dataIncluded[0]; case PF2: return !dataIncluded[1]; case PF3: return !dataIncluded[2]; case PF4: return !dataIncluded[3]; case PF5: return !dataIncluded[4]; case PF6: return !dataIncluded[5]; case PF7: return !dataIncluded[6]; case PF8: return !dataIncluded[7]; case PF9: return !dataIncluded[8]; case PF10: return !dataIncluded[9]; case PF11: return !dataIncluded[10]; case PF12: return !dataIncluded[11]; case PF13: return !dataIncluded[12]; case PF14: return !dataIncluded[13]; case PF15: return !dataIncluded[14]; case PF16: return !dataIncluded[15]; case PF17: return !dataIncluded[16]; case PF18: return !dataIncluded[17]; case PF19: return !dataIncluded[18]; case PF20: return !dataIncluded[19]; case PF21: return !dataIncluded[20]; case PF22: return !dataIncluded[21]; case PF23: return !dataIncluded[22]; case PF24: return !dataIncluded[23]; default: return true; } } /** * Help request - * * * See notes inside method */ public final void sendHelpRequest() { // Client sends header 000D12A0000004000003####F3FFEF // operation code 3 // row - first ## // column - second ## // F3 - Help Aid Key baosp.write(screen52.getCurrentRow()); baosp.write(screen52.getCurrentCol()); baosp.write(AID_HELP); try { Log.i(TAG, "sendHelpRequest() writeGDS()"); writeGDS(0, 3, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } baosp.reset(); } /** * Attention Key - * * * See notes inside method */ public final void sendAttentionKey() { // Client sends header 000A12A000004400000FFEF // 0x40 -> 01000000 // // flags // bit 0 - ERR // bit 1 - ATN Attention // bits 2-4 - reserved // bit 5 - SRQ system request // bit 6 - TRQ Test request key // bit 7 - HLP try { Log.i(TAG, "sendAttentionKey() writeGDS()"); writeGDS(0x40, 0, null); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } } /** * Opens a dialog and asks the user before sending a request * * @see {@link #systemRequest(String)} */ public final void systemRequest() { String ask = manager.res.getString(R.string.prompt_sys_request); String sysreq = bridge.promptHelper.requestStringPrompt(null, ask); systemRequest(sysreq); } /** * @param sr - system request option * @see {@link #systemRequest(String)} */ public final void systemRequest(char sr) { systemRequest(Character.toString(sr)); } /** * System request, taken from the rfc1205, 5250 Telnet interface section 4.3 * * @param sr system request option (allowed to be null, but than nothing happens) */ public final void systemRequest(String sr) { byte[] bytes = null; if ((sr != null) && (sr.length() > 0)) { // XXX: Not sure, if this is a sufficient check for 'clear dataq' if (sr.charAt(0) == '2') { dsq.clear(); } for (int i = 0, l = sr.length(); i < l; i++) { baosp.write(codePage.uni2ebcdic(sr.charAt(i))); } bytes = baosp.toByteArray(); } try { Log.i(TAG, "systemRequest() writeGDS()"); writeGDS(4, 0, bytes); } catch (IOException ioe) { Log.i(TAG, ioe.getMessage()); } baosp.reset(); } /** * Cancel Invite - taken from the rfc1205 - 5250 Telnet interface section * 4.3 * * See notes inside method */ public final void cancelInvite() { // screen52.setStatus(Screen5250.STATUS_SYSTEM, // Screen5250.STATUS_VALUE_ON, null); screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); // from rfc1205 section 4.3 // Server: Sends header with the 000A12A0 00000400 000AFFEF // Opcode = Cancel Invite. // Client: sends header with the 000A12A0 00000400 000AFFEF // Opcode = Cancel Invite to // indicate that the work station is // no longer invited. try { Log.i(TAG, "cancelInvite() writeGDS()"); writeGDS(0, 10, null); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } } public final void hostPrint(int aid) { if (screen52.isStatusErrorCode()) { screen52.restoreErrorLine(); screen52.setStatus(Screen5250.STATUS_ERROR_CODE, Screen5250.STATUS_VALUE_OFF, null); } screen52.setCursorActive(false); // screen52.setStatus(Screen5250.STATUS_SYSTEM, // Screen5250.STATUS_VALUE_ON, null); screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); // From client access ip capture // it seems to use an operation code of 3 and 4 // also note that the flag field that says reserved is being sent as // well // with a value of 0x80 // // I have tried with not setting these flags and sending with 3 or 1 // there is no effect and I still get a host print screen. Go figure //0000: 000D 12A0 0000 0400 8003 1407 F6FFEF //0000: 000D 12A0 0000 0400 8001 110E F6FFEF // // Client sends header 000D12A0000004000003####F6FFEF // operation code 3 // row - first ## // column - second ## // F6 - Print Aid Key baosp.write(screen52.getCurrentRow()); baosp.write(screen52.getCurrentCol()); baosp.write(AID_PRINT); // aid key try { Log.i(TAG, "hostPrint() writeGDS()"); writeGDS(0, 3, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } baosp.reset(); } public final void toggleDebug() { producer.toggleDebug(codePage); } // write gerneral data stream private final void writeGDS(int flags, int opcode, byte abyte0[]) throws IOException { // Added to fix for JDK 1.4 this was null coming from another method. // There was a weird keyRelease event coming from another panel when // using a key instead of the mouse to select button. // The other method was fixed as well but this check should be here // anyway. if (bout == null) return; int length; if (abyte0 != null) length = abyte0.length + 10; else length = 10; // refer to rfc1205 - 5250 Telnet interface // Section 3. Data Stream Format // Logical Record Length - 16 bits baosrsp.write(length >> 8); // Length LL baosrsp.write(length & 0xff); // LL // Record Type - 16 bits // It should always be set to '12A0'X to indicate the // General Data Stream (GDS) record type. baosrsp.write(18); // 0x12 baosrsp.write(160); // 0xA0 // the next 16 bits are not used baosrsp.write(0); // 0x00 baosrsp.write(0); // 0x00 // The second part is meant to be variable in length // currently this portion is 4 octets long (1 byte or 8 bits for us ;-O) baosrsp.write(4); // 0x04 baosrsp.write(flags); // flags // bit 0 - ERR // bit 1 - ATN Attention // bits 2-4 - reserved // bit 5 - SRQ system request // bit 6 - TRQ Test request key // bit 7 - HLP baosrsp.write(0); // reserved - set to 0x00 baosrsp.write(opcode); // opcode if (abyte0 != null) baosrsp.write(abyte0, 0, abyte0.length); baosrsp = appendByteStream(baosrsp.toByteArray()); // make sure we indicate no more to be sent baosrsp.write(IAC); baosrsp.write(EOR); baosrsp.writeTo(bout); bout.flush(); baosrsp.reset(); } protected final int getOpCode() { return bk.getOpCode(); } protected boolean[] getActiveAidKeys() { boolean aids[] = new boolean[dataIncluded.length]; System.arraycopy(dataIncluded, 0, aids, 0, dataIncluded.length); return aids; } private final void setInvited() { Log.d(TAG, "invited"); if (!screen52.isStatusErrorCode()) screen52.getOIA().setInputInhibited(ScreenOIA.INPUTINHIBITED_NOTINHIBITED, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED); invited = true; screen52.updateDirty(); buffer.testChanged(); } // WVL - LDC : 05/08/2005 : TFX.006253 - Support STRPCCMD private void strpccmd() { try { int str = 11; char c; ScreenPlanes planes = screen52.getPlanes(); c = planes.getChar(str); boolean waitFor = !(c == 'a'); StringBuilder command = new StringBuilder(); for (int i = str + 1; i < 132; i++) { c = planes.getChar(i); if (Character.isISOControl(c)) c = ' '; command.append(c); } String cmd = command.toString().trim(); run(cmd, waitFor); } finally { strpccmd = false; screen52.sendKeys(TN5250jConstants.MNEMONIC_ENTER); } } // WVL - LDC : 05/08/2005 : TFX.006253 - Support STRPCCMD private void run(String cmd, boolean waitFor) { try { Log.d(TAG, "RUN cmd = " + cmd); Log.d(TAG, "RUN wait = " + waitFor); Runtime r = Runtime.getRuntime(); Process p = r.exec(cmd); if (waitFor) { int result = p.waitFor(); Log.d(TAG, "RUN result = " + result); } } catch (Throwable t) { Log.e(TAG, "exception", t); } } // WVL - LDC : TR.000300 : Callback scenario from 5250 /** * Activate or deactivate the command scanning behaviour. * * @param scan * if true, scanning is enabled; disabled otherwise. * * @see #scan() */ public void setScanningEnabled(boolean scan) { this.scan = scan; } // WVL - LDC : TR.000300 : Callback scenario from 5250 /** * Checks whether command scanning is enabled. * * @return true is command scanning is enabled; false otherwise. */ public boolean isScanningEnabled() { return this.scan; } // WVL - LDC : TR.000300 : Callback scenario from 5250 /** * When command scanning is activated, the terminal reads the first and * second character in the datastream (the zero position allows to * devisualize the scan stream). If the sequence <code>#!</code> is * encountered and if this sequence is <strong>not </strong> followed by a * blank character, the {@link parseCommand()}is called. */ private void scan() { // screen52.screen[1].getChar() + screen52.screen[2].getChar()); // ScreenChar[] screen = screen52.screen; ScreenPlanes planes = screen52.getPlanes(); if ((planes.getChar(STRSCAN) == '#') && (planes.getChar(STRSCAN + 1) == '!') && (planes.getChar(STRSCAN + 2) != ' ')) { try { parseCommand(); } catch (Throwable t) { Log.i(TAG, "Exec cmd: " + t.getMessage()); t.printStackTrace(); } } } // WVL - LDC : TR.000300 : Callback scenario from 5250 /** * The screen is parsed starting from second position until a white space is * encountered. When found the Session#execCommand(String, int) is * called with the parsed string. The position immediately following the * encountered white space, separating the command from the rest of the * screen, is passed as starting index. * * Note that the character at the starting position can potentially be a * white space itself. The starting position in <code>execCommand</code> * provided to make the scanning sequence more flexible. We'd like for * example to embed also a <code>+</code> or <code>-</code> sign to * indicate whether the tnvt should trigger a repaint or not. This would * allow the flashing of command sequences without them becoming visible. * * <ul> * <li><strong>PRE </strong> The screen character at position * <code>STRSCAN + 2</code> is not a white space.</li> * </ul> */ private void parseCommand() { // Search for the command i.e. the first token in the stream // after the #! sequence separated by a space from the rest // of the screen. char[] screen = screen52.getScreenAsChars(); for (int s = STRSCAN + 2, i = s; i < screen.length; i++) { if (screen[i] == ' ') { String command = new String(screen, s, i - s); // Skip all white spaces between the command and the rest of // the screen. //for (; (i < screen.length) && (screen[i] == ' '); i++); String remainder = new String(screen, i + 1, screen.length - (i + 1)); //controller.fireScanned(command, remainder); Log.i(TAG, "trying to run " + command + " " + remainder); break; } } } public void run() { if (enhanced) sfParser = new WTDSFParser(this); bk = new Stream5250(); while (keepTrucking) { try { Object e = dsq.take(); if ((e instanceof Integer) && ((Integer)e == 0)) { screen52.updateDirty(); buffer.testChanged(); continue; }; bk.initialize((byte[])e); } catch (InterruptedException ie) { Log.w(TAG, " vt thread interrupted and stopping "); keepTrucking = false; continue; } invited = false; screen52.setCursorActive(false); if (bk == null) continue; switch (bk.getOpCode()) { case 00: Log.d(TAG, "No operation"); break; case 1: Log.d(TAG, "Invite Operation"); parseIncoming(); pendingUnlock = true; cursorOn = true; setInvited(); break; case 2: Log.d(TAG, "Output Only"); parseIncoming(); screen52.updateDirty(); break; case 3: Log.d(TAG, "Put/Get Operation"); parseIncoming(); setInvited(); break; case 4: Log.d(TAG, "Save Screen Operation"); parseIncoming(); break; case 5: Log.d(TAG, "Restore Screen Operation"); parseIncoming(); break; case 6: Log.d(TAG, "Read Immediate"); sendAidKey(0); break; case 7: Log.d(TAG, "Reserved"); break; case 8: Log.d(TAG, "Read Screen Operation"); try { readScreen(); } catch (IOException ex) { Log.w(TAG, ex.getMessage()); } break; case 9: Log.d(TAG, "Reserved"); break; case 10: Log.d(TAG, "Cancel Invite Operation"); cancelInvite(); break; case 11: Log.d(TAG, "Turn on message light"); screen52.getOIA().setMessageLightOn(); screen52.setCursorActive(true); break; case 12: Log.d(TAG, "Turn off Message light"); screen52.getOIA().setMessageLightOff(); screen52.setCursorActive(true); break; default: break; } if (screen52.isUsingGuiInterface()) screen52.drawFields(); try { if (!strpccmd) { screen52.updateDirty(); } else { strpccmd(); } } catch (RuntimeException e) { Log.w(TAG, " tnvt.run: " + e.getMessage()); e.printStackTrace(); } if (pendingUnlock && !screen52.isStatusErrorCode()) { screen52.getOIA().setKeyBoardLocked(false); pendingUnlock = false; } if (cursorOn && !screen52.getOIA().isKeyBoardLocked()) { screen52.setCursorActive(true); cursorOn = false; } } } public void dumpStuff() { Log.d(TAG, " Pending unlock " + pendingUnlock); Log.d(TAG, " Status Error " + screen52.isStatusErrorCode()); Log.d(TAG, " Keyboard Locked " + screen52.getOIA().isKeyBoardLocked()); Log.d(TAG, " Cursor On " + cursorOn); Log.d(TAG, " Cursor Active " + screen52.cursorActive); } private final void readScreen() throws IOException { int rows = screen52.getRows(); int cols = screen52.getColumns(); byte screenArray[] = new byte[rows * cols]; fillScreenArray(screenArray); Log.i(TAG, "readScreen() writeGDS()"); writeGDS(0, 0, screenArray); } private final void fillScreenArray(byte[] sa) { ScreenPlanes planes = screen52.planes; try { for (int i = 0; i < sa.length; i++) { // save the screen data if (planes.isAttributePlace(i)) { sa[i] = (byte) planes.getCharAttr(i); } else { // The characters on screen are in unicode char ch = planes.getChar(i); byte byteCh; if (isDataUnicode(ch)) byteCh = codePage.uni2ebcdic(ch); else byteCh = (byte) ch; sa[i] = byteCh; } } } catch (Exception exc) { Log.i(TAG, exc.getMessage()); exc.printStackTrace(); } } private byte[] createRegenerationBuffer(int len) throws IOException { int la = 32; int sac = 0; ScreenPlanes planes = screen52.planes; byte[] sa = new byte[len]; try { boolean guiExists = sfParser != null && sfParser.isGuisExists(); for (int i = 0; i < len; i++) { // save the screen data if (guiExists) { byte[] guiSeg = sfParser.getSegmentAtPos(i); if (guiSeg != null) { //Log.i(TAG," gui saved at " + y + " - " + screen52.getRow(y) + "," + // screen52.getCol(y)); byte[] gsa = new byte[sa.length + guiSeg.length + 2]; System.arraycopy(sa, 0, gsa, 0, sa.length); System.arraycopy(guiSeg, 0, gsa, sac + 2, guiSeg.length); sa = new byte[gsa.length]; System.arraycopy(gsa, 0, sa, 0, gsa.length); sa[sac++] = (byte)0x04; sa[sac++] = (byte)0x11; sac += guiSeg.length; } } if (planes.isAttributePlace(i)) { la = planes.getCharAttr(i); sa[sac++] = (byte) la; } else { // The characters on screen are in unicode char ch = planes.getChar(i); byte byteCh; if (isDataUnicode(ch)) byteCh = codePage.uni2ebcdic(ch); else byteCh = (byte) ch; sa[sac++] = byteCh; } } } catch (Exception exc) { Log.i(TAG, exc.getMessage()); exc.printStackTrace(); } return sa; } public final void saveScreen() throws IOException { ByteArrayOutputStream sc = new ByteArrayOutputStream(); sc.write(new byte[] {4, 0x12, 0, 0}); sc.write((byte) screen52.getRows()); // store the current size sc.write((byte) screen52.getColumns()); // "" int cp = screen52.getCurrentPos(); // save off current position sc.write((byte)(cp >> 8 & 0xff)); // "" sc.write((byte)(cp & 0xff)); // "" sc.write((byte)(screen52.homePos >> 8 & 0xff)); // save home pos sc.write((byte)(screen52.homePos & 0xff)); // "" sc.write(createRegenerationBuffer(screen52.getRows() * screen52.getColumns())); int sizeFields = screen52.getScreenFields().getSize(); sc.write((byte)(sizeFields >> 8 & 0xff)); // save number of fields sc.write((byte)(sizeFields & 0xff)); // "" if (sizeFields > 0) { int x = 0; int s = screen52.getScreenFields().getSize(); while (x < s) { ScreenField sf = screen52.getScreenFields().getField(x); sc.write((byte) sf.getAttr()); // attribute int sp = sf.startPos(); sc.write((byte)(sp >> 8 & 0xff)); // position sc.write((byte)(sp & 0xff)); // "" if (sf.mdt) sc.write((byte) 1); else sc.write((byte) 0); sc.write((byte)(sf.getLength() >> 8 & 0xff)); sc.write((byte)(sf.getLength() & 0xff)); sc.write((byte) sf.getFFW1() & 0xff); sc.write((byte) sf.getFFW2() & 0xff); sc.write((byte) sf.getFCW1() & 0xff); sc.write((byte) sf.getFCW2() & 0xff); Log.d(TAG, "Saved "); Log.d(TAG, sf.toString()); x++; } } // The following two lines of code looks to have caused all sorts of // problems so for now we have commented them out. // screen52.getScreenFields().setCurrentField(null); // set it to null // for GC ? // screen52.clearTable(); try { Log.i(TAG, "saveScreen() writeGDS()"); writeGDS(0, 3, sc.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } Log.d(TAG, "Save Screen end "); } /** * * @throws IOException */ public final void restoreScreen() throws IOException { int which = 0; ScreenPlanes planes = screen52.planes; try { Log.d(TAG, "Restore "); bk.getNextByte(); bk.getNextByte(); int rows = bk.getNextByte() & 0xff; int cols = bk.getNextByte() & 0xff; int pos = bk.getNextByte() << 8 & 0xff00; // current position pos |= bk.getNextByte() & 0xff; int hPos = bk.getNextByte() << 8 & 0xff00; // home position hPos |= bk.getNextByte() & 0xff; if ((rows != screen52.getRows()) || (cols != screen52.getColumns())) screen52.setRowsCols(rows, cols); screen52.clearAll(); // initialize what we currenty have if (sfParser != null && sfParser.isGuisExists()) sfParser.clearGuiStructs(); int b = 32; int la = 32; int len = rows * cols; for (int y = 0; y < len; y++) { b = bk.getNextByte(); if (b == 0x04) { Log.i(TAG, " gui restored at " + y + " - " + screen52.getRow(y) + "," + screen52.getCol(y)); int command = bk.getNextByte(); byte[] seg = bk.getSegment(); if (seg.length > 0) { screen52.goto_XY(y); sfParser.parseWriteToDisplayStructuredField(seg); } y--; // screen52.goto_XY(y); } else { // b = bk.getNextByte(); if (planes.isUseGui(y)) continue; if (isAttribute(b)) { planes.setScreenCharAndAttr(y, planes.getChar(y), b, true); la = b; } else { // The characters on screen are in unicode char ch; if (isDataEBCDIC(b)) ch = codePage.ebcdic2uni(b); else ch = (char) b; planes.setScreenCharAndAttr(y, ch, la, false); } } } int numFields = bk.getNextByte() << 8 & 0xff00; numFields |= bk.getNextByte() & 0xff; Log.d(TAG, "number of fields " + numFields); if (numFields > 0) { int x = 0; int attr = 0; int fPos = 0; int fLen = 0; int ffw1 = 0; int ffw2 = 0; int fcw1 = 0; int fcw2 = 0; boolean mdt = false; ScreenField sf = null; while (x < numFields) { attr = bk.getNextByte(); fPos = bk.getNextByte() << 8 & 0xff00; fPos |= bk.getNextByte() & 0xff; if (bk.getNextByte() == 1) mdt = true; else mdt = false; fLen = bk.getNextByte() << 8 & 0xff00; fLen |= bk.getNextByte() & 0xff; ffw1 = bk.getNextByte(); ffw2 = bk.getNextByte(); fcw1 = bk.getNextByte(); fcw2 = bk.getNextByte(); sf = screen52.getScreenFields().setField(attr, screen52.getRow(fPos), screen52.getCol(fPos), fLen, ffw1, ffw2, fcw1, fcw2); while (fLen-- > 0) { // now we set the field plane attributes planes.setScreenFieldAttr(fPos++, ffw1); } if (mdt) { sf.setMDT(); screen52.getScreenFields().setMasterMDT(); } Log.d(TAG, "Restored "); Log.d(TAG, sf.toString()); x++; } } // Redraw the gui fields if we are in gui mode if (screen52.isUsingGuiInterface()) screen52.drawFields(); screen52.restoreScreen(); // display the screen // The position was saved with currentPos which 1,1 offset of the // screen position. // The setPendingInsert is the where the cursor position will be // displayed after the restore. screen52.setPendingInsert(true, screen52.getRow(pos + cols), screen52 .getCol(pos + cols)); // We need to offset the pos by -1 since the position is 1,1 based // and the goto_XY is 0,0 based. screen52.goto_XY(pos - 1); screen52.isInField(); // // Redraw the gui fields if we are in gui mode // if (screen52.isUsingGuiInterface()) // screen52.drawFields(); } catch (Exception e) { Log.w(TAG, "error restoring screen " + which + " with " + e.getMessage()); } } public final boolean waitingForInput() { return waitingForInput; } private void parseIncoming() { boolean done = false; boolean error = false; try { while (bk.hasNext() && !done) { byte b = bk.getNextByte(); switch (b) { case 0: case 1: break; case CMD_SAVE_SCREEN: // 0x02 2 Save Screen case 3: // 0x03 3 Save Partial Screen Log.d(TAG, "save screen partial"); saveScreen(); break; case ESC: // ESCAPE break; case 7: // audible bell manager.playBeep(); bk.getNextByte(); bk.getNextByte(); break; case CMD_WRITE_TO_DISPLAY: // 0x11 17 write to display error = writeToDisplay(true); // WVL - LDC : TR.000300 : Callback scenario from 5250 // Only scan when WRITE_TO_DISPLAY operation (i.e. refill // screen buffer) // has been issued! if (scan) scan(); break; case CMD_RESTORE_SCREEN: // 0x12 18 Restore Screen case 13: // 0x13 19 Restore Partial Screen Log.d(TAG, "restore screen partial"); restoreScreen(); break; case CMD_CLEAR_UNIT_ALTERNATE: // 0x20 32 clear unit alternate int param = bk.getNextByte(); if (param != 0) { Log.d(TAG, " clear unit alternate error " + Integer.toHexString(param)); sendNegResponse(NR_REQUEST_ERROR, 03, 01, 05, " clear unit alternate not supported"); done = true; } else { if (screen52.getRows() != 27) screen52.setRowsCols(27, 132); screen52.clearAll(); if (sfParser != null && sfParser.isGuisExists()) sfParser.clearGuiStructs(); } break; case CMD_WRITE_ERROR_CODE: // 0x21 33 Write Error Code writeErrorCode(); error = writeToDisplay(false); break; case CMD_WRITE_ERROR_CODE_TO_WINDOW: // 0x22 34 // Write Error Code to window writeErrorCodeToWindow(); error = writeToDisplay(false); break; case CMD_READ_SCREEN_IMMEDIATE: // 0x62 98 case CMD_READ_SCREEN_TO_PRINT: // 0x66 102 read screen to print readScreen(); break; case CMD_CLEAR_UNIT: // 64 0x40 clear unit if (screen52.getRows() != 24) screen52.setRowsCols(24, 80); screen52.clearAll(); if (sfParser != null && sfParser.isGuisExists()) sfParser.clearGuiStructs(); break; case CMD_CLEAR_FORMAT_TABLE: // 80 0x50 Clear format table screen52.clearTable(); break; case CMD_READ_INPUT_FIELDS: //0x42 66 read input fields case CMD_READ_MDT_FIELDS: // 0x52 82 read MDT Fields bk.getNextByte(); bk.getNextByte(); readType = b; screen52.goHome(); // do nothing with the cursor here it is taken care of // in the main loop. //////////////// screen52.setCursorOn(); waitingForInput = true; pendingUnlock = true; // screen52.setKeyboardLocked(false); break; case CMD_READ_MDT_IMMEDIATE_ALT: // 0x53 83 readType = b; // screen52.goHome(); // waitingForInput = true; // screen52.setKeyboardLocked(false); readImmediate(readType); break; case CMD_WRITE_STRUCTURED_FIELD: // 243 0xF3 -13 Write // structured field writeStructuredField(); break; case CMD_ROLL: // 0x23 35 Roll Not sure what it does right now int updown = bk.getNextByte(); int topline = bk.getNextByte(); int bottomline = bk.getNextByte(); screen52.rollScreen(updown, topline, bottomline); break; default: done = true; sendNegResponse(NR_REQUEST_ERROR, 03, 01, 01, "parseIncoming"); break; } if (error) done = true; } } catch (Exception exc) { Log.w(TAG, "incoming " + exc.getMessage()); } } /** * This routine handles sending negative responses back to the host. * * You can find a description of the types of responses to be sent back by * looking at section 12.4 of the 5250 Functions Reference manual * * * @param cat * @param modifier * @param uByte1 * @param uByte2 * @param from * */ protected void sendNegResponse(int cat, int modifier, int uByte1, int uByte2, String from) { try { int os = bk.getByteOffset(-1) & 0xf0; int cp = (bk.getCurrentPos() - 1); Log.i(TAG, "invalid " + from + " command " + os + " at pos " + cp); } catch (Exception e) { Log.w(TAG, "Send Negative Response error " + e.getMessage()); } baosp.write(cat); baosp.write(modifier); baosp.write(uByte1); baosp.write(uByte2); try { Log.i(TAG, "sendNegResponse() writeGDS()"); writeGDS(128, 0, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } baosp.reset(); } public void sendNegResponse2(int ec) { screen52.setPrehelpState(true, true, false); baosp.write(0x00); baosp.write(ec); try { Log.i(TAG, "sendNegResponse2() writeGDS()"); writeGDS(1, 0, baosp.toByteArray()); } catch (IOException ioe) { Log.w(TAG, ioe.getMessage()); } baosp.reset(); } private boolean writeToDisplay(boolean controlsExist) { boolean error = false; boolean done = false; int attr; byte control0 = 0; byte control1 = 0; int saRows = screen52.getRows(); int saCols = screen52.getColumns(); try { if (controlsExist) { control0 = bk.getNextByte(); control1 = bk.getNextByte(); processCC0(control0); } while (bk.hasNext() && !done) { // pos = bk.getCurrentPos(); // int rowc = screen52.getCurrentRow(); // int colc = screen52.getCurrentCol(); byte bytebk = bk.getNextByte(); switch (bytebk) { case 1: // SOH - Start of Header Order Log.d(TAG, "SOH - Start of Header Order"); error = processSOH(); break; case 02: // RA - Repeat to address Log.d(TAG, "RA - Repeat to address"); int row = screen52.getCurrentRow(); int col = screen52.getCurrentCol(); int toRow = bk.getNextByte(); int toCol = bk.getNextByte() & 0xff; if (toRow >= row) { int repeat = bk.getNextByte(); // a little intelligence here I hope if (row == 1 && col == 2 && toRow == screen52.getRows() && toCol == screen52.getColumns()) screen52.clearScreen(); else { if (repeat != 0) { //LDC - 13/02/2003 - convert it to unicode repeat = codePage.ebcdic2uni(repeat); //repeat = getASCIIChar(repeat); } int times = ((toRow * screen52.getColumns()) + toCol) - ((row * screen52.getColumns()) + col); while (times-- >= 0) { screen52.setChar(repeat); } } } else { sendNegResponse(NR_REQUEST_ERROR, 0x05, 0x01, 0x23, " RA invalid"); error = true; } break; case 03: // EA - Erase to address Log.d(TAG, "EA - Erase to address"); int EArow = screen52.getCurrentRow(); int EAcol = screen52.getCurrentCol(); int toEARow = bk.getNextByte(); int toEACol = bk.getNextByte() & 0xff; int EALength = bk.getNextByte() & 0xff; while (--EALength > 0) { bk.getNextByte(); } char EAAttr = (char) 0; // a little intelligence here I hope if (EArow == 1 && EAcol == 2 && toEARow == screen52.getRows() && toEACol == screen52.getColumns()) screen52.clearScreen(); else { int times = ((toEARow * screen52.getColumns()) + toEACol) - ((EArow * screen52.getColumns()) + EAcol); while (times-- >= 0) { screen52.setChar(EAAttr); } } break; case 04: // Command - Escape Log.d(TAG, "Command - Escape"); done = true; break; case 16: // TD - Transparent Data Log.d(TAG, "TD - Transparent Data"); int j = (bk.getNextByte() & 0xff) << 8 | bk.getNextByte() & 0xff; // length break; case 17: // SBA - set buffer address order (row column) //Log.d(TAG,"SBA - set buffer address order (row column)"); int saRow = bk.getNextByte(); int saCol = bk.getNextByte() & 0xff; // make sure it is in bounds if (saRow >= 0 && saRow <= screen52.getRows() && saCol >= 0 && saCol <= screen52.getColumns()) { screen52.setCursor(saRow, saCol); // now set screen // position for output } else { sendNegResponse(NR_REQUEST_ERROR, 0x05, 0x01, 0x22, "invalid row/col order" + " saRow = " + saRow + " saRows = " + screen52.getRows() + " saCol = " + saCol); error = true; } break; case 18: // WEA - Extended Attribute Log.d(TAG, "WEA - Extended Attribute"); bk.getNextByte(); bk.getNextByte(); break; case 19: // IC - Insert Cursor Log.d(TAG, "IC - Insert Cursor"); int icX = bk.getNextByte(); int icY = bk.getNextByte() & 0xff; if (icX >= 0 && icX <= saRows && icY >= 0 && icY <= saCols) { Log.d(TAG, " IC " + icX + " " + icY); screen52.setPendingInsert(true, icX, icY); } else { sendNegResponse(NR_REQUEST_ERROR, 0x05, 0x01, 0x22, " IC/IM position invalid "); error = true; } break; case 20: // MC - Move Cursor Log.d(TAG, "MC - Move Cursor"); int imcX = bk.getNextByte(); int imcY = bk.getNextByte() & 0xff; if (imcX >= 0 && imcX <= saRows && imcY >= 0 && imcY <= saCols) { Log.d(TAG, " MC " + imcX + " " + imcY); screen52.setPendingInsert(false, imcX, imcY); } else { sendNegResponse(NR_REQUEST_ERROR, 0x05, 0x01, 0x22, " IC/IM position invalid "); error = true; } break; case 21: // WTDSF - Write To Display Structured Field order Log.d(TAG, "WTDSF - Write To Display Structured Field order"); byte[] seg = bk.getSegment(); error = sfParser.parseWriteToDisplayStructuredField(seg); break; case 29: // SF - Start of Field Log.d(TAG, "SF - Start of Field"); int fcw1 = 0; int fcw2 = 0; int ffw1 = 0; int ffw0 = bk.getNextByte() & 0xff; // FFW if ((ffw0 & 0x40) == 0x40) { ffw1 = bk.getNextByte() & 0xff; // FFW 1 fcw1 = bk.getNextByte() & 0xff; // check for field // control word // check if the first fcw1 is an 0x81 if it is then get // the next pair for checking if (fcw1 == 0x81) { bk.getNextByte(); fcw1 = bk.getNextByte() & 0xff; // check for field // control word } if (!isAttribute(fcw1)) { fcw2 = bk.getNextByte() & 0xff; // FCW 2 attr = bk.getNextByte() & 0xff; // attribute field while (!isAttribute(attr)) { Log.i(TAG, Integer.toHexString(fcw1) + " " + Integer.toHexString(fcw2) + " "); Log.i(TAG, Integer.toHexString(attr) + " " + Integer.toHexString(bk .getNextByte() & 0xff)); attr = bk.getNextByte() & 0xff; // attribute field } } else { attr = fcw1; // attribute of field fcw1 = 0; } } else { attr = ffw0; } int fLength = (bk.getNextByte() & 0xff) << 8 | bk.getNextByte() & 0xff; screen52.addField(attr, fLength, ffw0, ffw1, fcw1, fcw2); break; // WVL - LDC : 05/08/2005 : TFX.006253 - Support STRPCCMD case -128: //STRPCCMD // if (screen52.getCurrentPos() == 82) { Log.d(TAG, "STRPCCMD got a -128 command at " + screen52.getCurrentPos()); StringBuffer value = new StringBuffer(); int b; char c; int[] pco = new int[9]; int[] pcoOk = {0xfc, 0xd7, 0xc3, 0xd6, 0x40, 0x83, 0x80, 0xa1, 0x80}; for (int i = 0; i < 9; i++) { b = bk.getNextByte(); pco[i] = ((b & 0xff)); c = codePage.ebcdic2uni(b); value.append(c); } // Check "PCO-String" if (Arrays.equals(pco, pcoOk)) { strpccmd = true; } // we return in the stream to have all chars // arrive at the screen for later processing for (int i = 0; i < 9; i++) bk.setPrevByte(); //} // no break: so every chars arrives // on the screen for later parsing //break; default: // all others must be output to screen byte byte0 = bk.getByteOffset(-1); if (isAttribute(byte0)) { screen52.setAttr(byte0); } else { if (!screen52.isStatusErrorCode()) { if (!isDataEBCDIC(byte0)) { // if (byte0 == 255) { // sendNegResponse(NR_REQUEST_ERROR,0x05,0x01,0x42, // " Attempt to send FF to screen"); // } // else screen52.setChar(byte0); } else //LDC - 13/02/2003 - Convert it to unicode //screen52.setChar(getASCIIChar(byte0)); screen52.setChar(codePage.ebcdic2uni(byte0)); } else { if (byte0 == 0) screen52.setChar(byte0); else //LDC - 13/02/2003 - Convert it to unicode //screen52.setChar(getASCIIChar(byte0)); screen52.setChar(codePage.ebcdic2uni(byte0)); } } break; } if (error) done = true; } } catch (Exception e) { Log.w(TAG, "write to display " + e.getMessage()); } ; processCC1(control1); return error; } private boolean processSOH() throws Exception { int l = bk.getNextByte(); // length Log.d(TAG, " byte 0 " + l); if (l > 0 && l <= 7) { bk.getNextByte(); // flag byte 2 bk.getNextByte(); // reserved bk.getNextByte(); // resequence fields screen52.clearTable(); // well that is the first time I have seen this. This fixes a // problem with S/36 command line. Finally got it. if (l <= 3) return false; screen52.setErrorLine(bk.getNextByte()); // error row int byte1 = 0; if (l >= 5) { byte1 = bk.getNextByte(); dataIncluded[23] = (byte1 & 0x80) == 0x80; dataIncluded[22] = (byte1 & 0x40) == 0x40; dataIncluded[21] = (byte1 & 0x20) == 0x20; dataIncluded[20] = (byte1 & 0x10) == 0x10; dataIncluded[19] = (byte1 & 0x8) == 0x8; dataIncluded[18] = (byte1 & 0x4) == 0x4; dataIncluded[17] = (byte1 & 0x2) == 0x2; dataIncluded[16] = (byte1 & 0x1) == 0x1; } if (l >= 6) { byte1 = bk.getNextByte(); dataIncluded[15] = (byte1 & 0x80) == 0x80; dataIncluded[14] = (byte1 & 0x40) == 0x40; dataIncluded[13] = (byte1 & 0x20) == 0x20; dataIncluded[12] = (byte1 & 0x10) == 0x10; dataIncluded[11] = (byte1 & 0x8) == 0x8; dataIncluded[10] = (byte1 & 0x4) == 0x4; dataIncluded[9] = (byte1 & 0x2) == 0x2; dataIncluded[8] = (byte1 & 0x1) == 0x1; } if (l >= 7) { byte1 = bk.getNextByte(); dataIncluded[7] = (byte1 & 0x80) == 0x80; dataIncluded[6] = (byte1 & 0x40) == 0x40; dataIncluded[5] = (byte1 & 0x20) == 0x20; dataIncluded[4] = (byte1 & 0x10) == 0x10; dataIncluded[3] = (byte1 & 0x8) == 0x8; dataIncluded[2] = (byte1 & 0x4) == 0x4; dataIncluded[1] = (byte1 & 0x2) == 0x2; dataIncluded[0] = (byte1 & 0x1) == 0x1; } return false; } else { sendNegResponse(NR_REQUEST_ERROR, 0x05, 0x01, 0x2B, "invalid SOH length"); return true; } } private void processCC0(byte byte0) { Log.d(TAG, " Control byte0 " + Integer.toBinaryString(byte0 & 0xff)); boolean lockKeyboard = true; boolean resetMDT = false; boolean resetMDTAll = false; boolean nullMDT = false; boolean nullAll = false; // Bits 3 to 6 are reserved and should be set to '0000' // 0xE0 = '11100000' - only the first 3 bits are tested if ((byte0 & 0xE0) == 0x00) { lockKeyboard = false; } // '00100000' = 0x20 /32 -- just lock keyboard // '01000000' = 0x40 /64 // '01100000' = 0x60 /96 // '10000000' = 0x80 /128 // '10100000' = 0xA0 /160 // '11000000' = 0xC0 /192 // '11100000' = 0xE0 /224 switch (byte0 & 0xE0) { case 0x40: resetMDT = true; break; case 0x60: resetMDTAll = true; break; case 0x80: nullMDT = true; break; case 0xA0: resetMDT = true; nullAll = true; break; case 0xC0: resetMDT = true; nullMDT = true; break; case 0xE0: resetMDTAll = true; nullAll = true; break; } if (lockKeyboard) { screen52.getOIA().setKeyBoardLocked(true); pendingUnlock = false; } else pendingUnlock = false; if (resetMDT || resetMDTAll || nullMDT || nullAll) { int f = screen52.getScreenFields().getSize(); for (int x = 0; x < f; x++) { ScreenField sf = screen52.getScreenFields().getField(x); if (!sf.isBypassField()) { if ((nullMDT && sf.mdt) || nullAll) { sf.setFieldChar((char) 0x0); screen52.drawField(sf); } } if (resetMDTAll || (resetMDT && !sf.isBypassField())) sf.resetMDT(); } } } private void processCC1(byte byte1) { Log.d(TAG, " Control byte1 " + Integer.toBinaryString(byte1 & 0xff)); if ((byte1 & 0x04) == 0x04) { manager.playBeep(); } if ((byte1 & 0x02) == 0x02) { screen52.getOIA().setMessageLightOff(); } if ((byte1 & 0x01) == 0x01) { screen52.getOIA().setMessageLightOn(); } if ((byte1 & 0x01) == 0x01 && (byte1 & 0x02) == 0x02) { screen52.getOIA().setMessageLightOn(); } // reset blinking cursor seems to control whether to set or not set the // the cursor position. No documentation for this just testing and // looking at the bit settings of this field. This was a pain in the // ass! // // if it is off '0' then keep existing cursor positioning information // if it is on '1' then reset the cursor positioning information // *** Note *** unless we receive bit 4 on at the same time // this seems to work so far if ((byte1 & 0x20) == 0x20 && (byte1 & 0x08) == 0x00) { screen52.setPendingInsert(false); Log.d(TAG, " WTD position no move"); } else { screen52.setPendingInsert(true); Log.d(TAG, " WTD position move to home" + screen52.homePos + " row " + screen52.getRow(screen52.homePos) + " col " + screen52.getCol(screen52.homePos)); } // in enhanced mode we sometimes only receive bit 6 turned on which // is reset blinking cursor if ((byte1 & 0x20) == 0x20 && enhanced) { cursorOn = true; } if (!screen52.isStatusErrorCode() && (byte1 & 0x08) == 0x08) { // screen52.setStatus(screen52.STATUS_SYSTEM,screen52.STATUS_VALUE_OFF,null); cursorOn = true; } if ((byte1 & 0x20) == 0x20 && (byte1 & 0x08) == 0x00) { screen52.setPendingInsert(false, 1, 1); } } private boolean isAttribute(int byte0) { int byte1 = byte0 & 0xff; return (byte1 & 0xe0) == 0x20; } //LDC - 12/02/2003 - Function name changed from isData to isDataEBCDIC private boolean isDataEBCDIC(int byte0) { int byte1 = byte0 & 0xff; // here it should always be less than 255 if (byte1 >= 64 && byte1 < 255) return true; else return false; } //LDC - 12/02/2003 - Test if the unicode character is a displayable // character. // The first 32 characters are non displayable characters // This is normally the inverse of isDataEBCDIC (That's why there is a // check on 255 -> 0xFFFF private boolean isDataUnicode(int data) { return (((data < 0) || (data >= 32)) && (data != 0xFFFF)); } private void writeStructuredField() { boolean done = false; try { int length = ((bk.getNextByte() & 0xff) << 8 | (bk.getNextByte() & 0xff)); while (bk.hasNext() && !done) { switch (bk.getNextByte()) { case -39: // SOH - Start of Header Order switch (bk.getNextByte()) { case 112: // 5250 Query bk.getNextByte(); // get null required field sendQueryResponse(); break; default: Log.d(TAG, "invalid structured field sub command " + bk.getByteOffset(-1)); break; } break; default: Log.d(TAG, "invalid structured field command " + bk.getByteOffset(-1)); break; } } } catch (Exception e) { } ; } private final void writeErrorCode() throws Exception { screen52.setCursor(screen52.getErrorLine(), 1); // Skip the control byte screen52.setStatus(Screen5250.STATUS_ERROR_CODE, Screen5250.STATUS_VALUE_ON, null); screen52.saveErrorLine(); cursorOn = true; } private final void writeErrorCodeToWindow() throws Exception { int fromCol = bk.getNextByte() & 0xff; // from column int toCol = bk.getNextByte() & 0xff; // to column screen52.setCursor(screen52.getErrorLine(), fromCol); // Skip the control byte screen52.setStatus(Screen5250.STATUS_ERROR_CODE, Screen5250.STATUS_VALUE_ON, null); screen52.saveErrorLine(); cursorOn = true; } /** * Method sendQueryResponse * * The query command is used to obtain information about the capabilities of * the 5250 display. * * The Query command must follow an Escape (0x04) and Write Structured Field * command (0xF3). * * This section is modeled after the rfc1205 - 5250 Telnet Interface section * 5.3 * * @throws IOException */ private final void sendQueryResponse() throws IOException { Log.i(TAG, "sending query response"); byte abyte0[] = new byte[64]; abyte0[0] = 0; // Cursor Row/column (set to zero) abyte0[1] = 0; // "" abyte0[2] = -120; // X'88' inbound write structure Field aid if (enhanced) { abyte0[3] = 0; // 0x003D (61) length of query response abyte0[4] = 64; // "" see note below ????????? } else { abyte0[3] = 0; // 0x003A (58) length of query response abyte0[4] = 58; // "" // the length between 58 and 64 seems to cause // different formatting codes to be sent from // the host ???????????????? why ??????? // Well the why can be found in the manual if // read a little more ;-) } abyte0[5] = -39; // command class 0xD9 abyte0[6] = 112; // Command type query 0x70 abyte0[7] = -128; // 0x80 Flag byte abyte0[8] = 6; // Controller Hardware Class abyte0[9] = 0; // 0x0600 - Other WSF or another 5250 Emulator abyte0[10] = 1; // Controller Code Level abyte0[11] = 1; // Version 1 Rel 1.0 abyte0[12] = 0; // "" abyte0[13] = 0; // 13 - 28 are reserved so set to 0x00 abyte0[14] = 0; // "" abyte0[15] = 0; // "" abyte0[16] = 0; // "" abyte0[17] = 0; // "" abyte0[18] = 0; // "" abyte0[19] = 0; // "" abyte0[20] = 0; // "" abyte0[21] = 0; // "" abyte0[22] = 0; // "" abyte0[23] = 0; // "" abyte0[24] = 0; // "" abyte0[25] = 0; // "" abyte0[26] = 0; // "" abyte0[27] = 0; // "" abyte0[28] = 0; // "" abyte0[29] = 1; // Device type - 0x01 5250 Emulator abyte0[30] = codePage.uni2ebcdic('5'); // Device type character abyte0[31] = codePage.uni2ebcdic('2'); // "" abyte0[32] = codePage.uni2ebcdic('5'); // "" abyte0[33] = codePage.uni2ebcdic('1'); // "" abyte0[34] = codePage.uni2ebcdic('0'); // "" abyte0[35] = codePage.uni2ebcdic('1'); // "" abyte0[36] = codePage.uni2ebcdic('1'); // "" abyte0[37] = 2; // Keyboard Id - 0x02 Standard Keyboard abyte0[38] = 0; // extended keyboard id abyte0[39] = 0; // reserved abyte0[40] = 0; // 40 - 43 Display Serial Number abyte0[41] = 36; // abyte0[42] = 36; // abyte0[43] = 0; // abyte0[44] = 1; // Maximum number of display fields - 256 abyte0[45] = 0; // 0x0100 abyte0[46] = 0; // 46 -48 Reserved set to 0x00 abyte0[47] = 0; abyte0[48] = 0; abyte0[49] = 1; // 49 - 53 Controller Display Capability abyte0[50] = 17; // see rfc - tired of typing :-) abyte0[51] = 0; // "" abyte0[52] = 0; // "" // 53 // Bit 0-2: B'000' - no graphics capability // B'001' - 5292-2 style graphics // Bit 3-7: B '00000' = reserved (it seems for Client access) if (enhanced) { // abyte0[53] = 0x5E; // 0x5E turns on enhanced mode // abyte0[53] = 0x27; // 0x5E turns on enhanced mode abyte0[53] = 0x7; // 0x5E turns on ehnhanced mode Log.i(TAG, "enhanced options"); } else abyte0[53] = 0x0; // 0x0 is normal emulation abyte0[54] = 24; // 54 - 60 Reserved set to 0x00 // 54 - I found out is used for enhanced user // interface level 3. Bit 4 allows headers // and footers for windows abyte0[54] = 8; // 54 - 60 Reserved set to 0x00 // 54 - I found out is used for enhanced user // interface level 3. Bit 4 allows headers // and footers for windows abyte0[55] = 0; abyte0[56] = 0; abyte0[57] = 0; abyte0[58] = 0; abyte0[59] = 0; abyte0[60] = 0; abyte0[61] = 0; // gridlines are not supported abyte0[62] = 0; // gridlines are not supported abyte0[63] = 0; Log.i(TAG, "sendQueryResponse() writeGDS()"); writeGDS(0, 0, abyte0); // now tell them about us abyte0 = null; } protected final boolean negotiate(byte abyte0[]) throws IOException { int i = 0; // from server negotiations if (abyte0[i] == IAC) { // -1 while (i < abyte0.length && abyte0[i++] == -1) // while(i < abyte0.length && (abyte0[i] == -1 || abyte0[i++] == 0x20)) switch (abyte0[i++]) { // we will not worry about what it WONT do case WONT: // -4 default: break; case DO: //-3 // not sure why but since moving to V5R2 we are receiving a // DO with no option when connecting a second session with // device name. Can not find the cause at all. If anybody // is interested please debug this until then this works. if (i < abyte0.length) { switch (abyte0[i]) { case TERMINAL_TYPE: // 24 baosp.write(IAC); baosp.write(WILL); baosp.write(TERMINAL_TYPE); writeByte(baosp.toByteArray()); baosp.reset(); break; case OPT_END_OF_RECORD: // 25 baosp.write(IAC); baosp.write(WILL); baosp.write(OPT_END_OF_RECORD); writeByte(baosp.toByteArray()); baosp.reset(); break; case TRANSMIT_BINARY: // 0 baosp.write(IAC); baosp.write(WILL); baosp.write(TRANSMIT_BINARY); writeByte(baosp.toByteArray()); baosp.reset(); break; case TIMING_MARK: // 6 rfc860 Log.d(TAG, "Timing Mark Received and notifying the server that we will not do it."); baosp.write(IAC); baosp.write(WONT); baosp.write(TIMING_MARK); writeByte(baosp.toByteArray()); baosp.reset(); break; case NEW_ENVIRONMENT: // 39 rfc1572, rfc4777 // allways send new environment vars ... baosp.write(IAC); baosp.write(WILL); baosp.write(NEW_ENVIRONMENT); writeByte(baosp.toByteArray()); baosp.reset(); break; default: // every thing else we will not do at this time baosp.write(IAC); baosp.write(WONT); baosp.write(abyte0[i]); // either writeByte(baosp.toByteArray()); baosp.reset(); break; } } i++; break; case WILL: switch (abyte0[i]) { case OPT_END_OF_RECORD: // 25 baosp.write(IAC); baosp.write(DO); baosp.write(OPT_END_OF_RECORD); writeByte(baosp.toByteArray()); baosp.reset(); break; case TRANSMIT_BINARY: // '\0' baosp.write(IAC); baosp.write(DO); baosp.write(TRANSMIT_BINARY); writeByte(baosp.toByteArray()); baosp.reset(); break; } i++; break; case SB: // -6 if (abyte0[i] == NEW_ENVIRONMENT && abyte0[i + 1] == 1) { negNewEnvironment(); while (++i < abyte0.length && abyte0[i + 1] != IAC); } if (abyte0[i] == TERMINAL_TYPE && abyte0[i + 1] == 1) { baosp.write(IAC); baosp.write(SB); baosp.write(TERMINAL_TYPE); baosp.write(QUAL_IS); if (!support132) baosp.write("IBM-3179-2".getBytes()); else baosp.write("IBM-3477-FC".getBytes()); baosp.write(IAC); baosp.write(SE); writeByte(baosp.toByteArray()); baosp.reset(); i++; } i++; break; } return true; } else { return false; } } /** * Negotiate new environment string for device name * * @throws IOException */ private void negNewEnvironment() throws IOException { baosp.write(IAC); baosp.write(SB); baosp.write(NEW_ENVIRONMENT); baosp.write(IS); // http://tools.ietf.org/html/rfc4777 if (kbdTypesCodePage != null) { baosp.write(USERVAR); baosp.write("KBDTYPE".getBytes()); baosp.write(VALUE); baosp.write(kbdTypesCodePage.kbdType.getBytes()); baosp.write(USERVAR); baosp.write("CODEPAGE".getBytes()); baosp.write(VALUE); baosp.write(kbdTypesCodePage.codepage.getBytes()); baosp.write(USERVAR); baosp.write("CHARSET".getBytes()); baosp.write(VALUE); baosp.write(kbdTypesCodePage.charset.getBytes()); } if (devName != null) { baosp.write(USERVAR); baosp.write("DEVNAME".getBytes()); baosp.write(VALUE); baosp.write(negDeviceName().getBytes()); } if (user != null) { baosp.write(VAR); baosp.write("USER".getBytes()); baosp.write(VALUE); baosp.write(user.getBytes()); if (password != null) { baosp.write(USERVAR); baosp.write("IBMRSEED".getBytes()); baosp.write(VALUE); baosp.write(NEGOTIATE_ESC); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(0x0); baosp.write(USERVAR); baosp.write("IBMSUBSPW".getBytes()); baosp.write(VALUE); baosp.write(password.getBytes()); } if (library != null) { baosp.write(USERVAR); baosp.write("IBMCURLIB".getBytes()); baosp.write(VALUE); baosp.write(library.getBytes()); } if (initialMenu != null) { baosp.write(USERVAR); baosp.write("IBMIMENU".getBytes()); baosp.write(VALUE); baosp.write(initialMenu.getBytes()); } if (program != null) { baosp.write(USERVAR); baosp.write("IBMPROGRAM".getBytes()); baosp.write(VALUE); baosp.write(program.getBytes()); } } baosp.write(IAC); baosp.write(SE); writeByte(baosp.toByteArray()); baosp.reset(); } /** * This will negotiate a device name with controller. if the sequence is * less than zero then it will send the device name as specified. On each * unsuccessful attempt a sequential number is appended until we find one or * the controller says no way. * * @return String */ private String negDeviceName() { if (devSeq++ == -1) { devNameUsed = devName; return devName; } else { StringBuilder sb = new StringBuilder(devName + devSeq); int ei = 1; while (sb.length() > 10) { sb.setLength(0); sb.append(devName.substring(0, devName.length() - ei++)); sb.append(devSeq); } devNameUsed = sb.toString(); return devNameUsed; } } public final void setCodePage(String cp) { codePage = CharMappings.getCodePage(cp); cp = cp.toLowerCase(); for (KbdTypesCodePages kbdtyp : KbdTypesCodePages.values()) { if (("cp" + kbdtyp.codepage).equals(cp) || kbdtyp.ccsid.equals(cp)) { kbdTypesCodePage = kbdtyp; break; } } Log.i(TAG, "Chose keyboard mapping " + kbdTypesCodePage.toString() + " for code page " + cp); } public final ICodePage getCodePage() { return codePage; } public void signalBell() { manager.playBeep(); } }