diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/org/connectbot/AboutActivity.java | 17 | ||||
-rw-r--r-- | src/org/connectbot/Console.java | 145 | ||||
-rw-r--r-- | src/org/connectbot/FrontPage.java | 2 | ||||
-rw-r--r-- | src/org/connectbot/HostList.java | 86 | ||||
-rw-r--r-- | src/org/connectbot/R.java | 24 | ||||
-rw-r--r-- | src/org/connectbot/SettingsActivity.java | 16 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalBridge.java | 264 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalManager.java | 64 | ||||
-rw-r--r-- | src/org/connectbot/util/HostDatabase.java | 7 | ||||
-rw-r--r-- | src/org/theb/ssh/HostsList.java | 2 |
10 files changed, 480 insertions, 147 deletions
diff --git a/src/org/connectbot/AboutActivity.java b/src/org/connectbot/AboutActivity.java new file mode 100644 index 0000000..a62c0ed --- /dev/null +++ b/src/org/connectbot/AboutActivity.java @@ -0,0 +1,17 @@ +package org.connectbot; + +import android.app.Activity; +import android.os.Bundle; + +import org.connectbot.R; + + +public class AboutActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.act_about); + + } +}
\ No newline at end of file diff --git a/src/org/connectbot/Console.java b/src/org/connectbot/Console.java index 4493ac7..0561848 100644 --- a/src/org/connectbot/Console.java +++ b/src/org/connectbot/Console.java @@ -15,16 +15,21 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.text.ClipboardManager; import android.util.Log; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.MenuItem.OnMenuItemClickListener; import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.animation.Animation; @@ -49,7 +54,26 @@ public class Console extends Activity { // clear out any existing bridges and record requested index flip.removeAllViews(); + String requestedNickname = (requested != null) ? requested.getFragment() : null; int requestedIndex = 0; + + + // first check if we need to create a new session for requested + boolean found = false; + for(TerminalBridge bridge : bound.bridges) { + if(bridge.nickname.equals(requestedNickname)) + found = true; + } + + if(!found) { + try { + Log.d(this.getClass().toString(), "onServiceConnected() is openConnection() because of unknown requested uri="+requested.toString()); + bound.openConnection(requested); + } catch(Exception e) { + e.printStackTrace(); + } + } + // create views for all bridges on this service for(TerminalBridge bridge : bound.bridges) { @@ -59,7 +83,7 @@ public class Console extends Activity { // set the terminal overlay text TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay); - overlay.setText(bridge.overlay); + overlay.setText(bridge.nickname); // and add our terminal view control, using index to place behind overlay TerminalView terminal = new TerminalView(Console.this, bridge); @@ -70,7 +94,7 @@ public class Console extends Activity { flip.addView(view); // check to see if this bridge was requested - if(bridge.overlay.equals(requestedBridge)) + if(bridge.nickname.equals(requestedNickname)) requestedIndex = flip.getChildCount() - 1; } @@ -89,7 +113,7 @@ public class Console extends Activity { }; public Animation fade_out = null; - public String requestedBridge = null; + //public String requestedBridge = null; public void updateDefault() { // update the current default terminal @@ -98,6 +122,33 @@ public class Console extends Activity { bound.defaultBridge = terminal.bridge; } + public Uri requested; + + @Override + public void onStart() { + super.onStart(); + + // connect with manager service to find all bridges + // when connected it will insert all views + this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + + } + + @Override + public void onStop() { + super.onStop(); + this.unbindService(connection); + + } + + protected View findCurrentView(int id) { + View view = this.flip.getCurrentView(); + if(view == null) return null; + return view.findViewById(id); + } + + public ClipboardManager clipboard; + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -105,18 +156,19 @@ public class Console extends Activity { this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.setContentView(R.layout.act_console); + this.clipboard = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE); + // pull out any requested bridge - this.requestedBridge = this.getIntent().getExtras().getString(Intent.EXTRA_TEXT); + //this.requestedBridge = this.getIntent().getExtras().getString(Intent.EXTRA_TEXT); + + // handle requested console from incoming intent + this.requested = this.getIntent().getData(); this.inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.flip = (ViewFlipper)this.findViewById(R.id.console_flip); this.fade_out = AnimationUtils.loadAnimation(this, R.anim.fade_out); - - // connect with manager service to find all bridges - // when connected it will insert all views - this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); - + // preload animations for terminal switching final Animation slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); @@ -141,7 +193,9 @@ public class Console extends Activity { // activate consider if within x tolerance if(Math.abs(e1.getX() - e2.getX()) < 100) { - TerminalView terminal = (TerminalView)flip.getCurrentView().findViewById(R.id.console_flip); + View flip = findCurrentView(R.id.console_flip); + if(flip == null) return false; + TerminalView terminal = (TerminalView)flip; // estimate how many rows we have scrolled through // accumulate distance that doesn't trigger immediate scroll @@ -193,7 +247,8 @@ public class Console extends Activity { if(distx > goalwidth) { // keep current overlay from popping up again - flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_stay_hidden); + View overlay = findCurrentView(R.id.terminal_overlay); + if(overlay != null) overlay.startAnimation(fade_stay_hidden); flip.setInAnimation(slide_right_in); flip.setOutAnimation(slide_right_out); @@ -201,14 +256,16 @@ public class Console extends Activity { Console.this.updateDefault(); // show overlay on new slide and start fade - flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_out); + overlay = findCurrentView(R.id.terminal_overlay); + if(overlay != null) overlay.startAnimation(fade_out); return true; } if(distx < -goalwidth) { // keep current overlay from popping up again - flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_stay_hidden); + View overlay = findCurrentView(R.id.terminal_overlay); + if(overlay != null) overlay.startAnimation(fade_stay_hidden); flip.setInAnimation(slide_left_in); flip.setOutAnimation(slide_left_out); @@ -216,7 +273,8 @@ public class Console extends Activity { Console.this.updateDefault(); // show overlay on new slide and start fade - flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_out); + overlay = findCurrentView(R.id.terminal_overlay); + if(overlay != null) overlay.startAnimation(fade_out); return true; } @@ -245,5 +303,64 @@ public class Console extends Activity { } + + public MenuItem copy, paste; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + MenuItem add = menu.add(0, 0, Menu.NONE, "Disconnect"); + add.setIcon(android.R.drawable.ic_menu_close_clear_cancel); + add.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // close the currently visible session + View view = findCurrentView(R.id.console_flip); + if(view == null) return false; + TerminalView terminal = (TerminalView)view; + bound.disconnect(terminal.bridge); + flip.removeView(flip.getCurrentView()); + return true; + } + }); + + copy = menu.add(0, 0, Menu.NONE, "Copy"); + copy.setIcon(android.R.drawable.ic_menu_set_as); + copy.setEnabled(false); + + + paste = menu.add(0, 0, Menu.NONE, "Paste"); + paste.setIcon(android.R.drawable.ic_menu_edit); + paste.setEnabled(clipboard.hasText()); + paste.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // force insert of clipboard text into current console + View view = findCurrentView(R.id.console_flip); + if(view == null) return false; + TerminalView terminal = (TerminalView)view; + + // pull string from clipboard and generate all events to force down + String clip = clipboard.getText().toString(); + KeyEvent[] events = terminal.bridge.keymap.getEvents(clip.toCharArray()); + for(KeyEvent event : events) { + terminal.bridge.onKey(terminal, event.getKeyCode(), event); + } + + return true; + } + }); + + return true; + + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + paste.setEnabled(clipboard.hasText()); + + return true; + } } diff --git a/src/org/connectbot/FrontPage.java b/src/org/connectbot/FrontPage.java index 4fa7734..e854c13 100644 --- a/src/org/connectbot/FrontPage.java +++ b/src/org/connectbot/FrontPage.java @@ -27,7 +27,7 @@ import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; -import org.theb.ssh.R; + public class FrontPage extends Activity { diff --git a/src/org/connectbot/HostList.java b/src/org/connectbot/HostList.java index 3689fbc..446346a 100644 --- a/src/org/connectbot/HostList.java +++ b/src/org/connectbot/HostList.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.view.ContextMenu; @@ -47,20 +48,8 @@ public class HostList extends Activity { public void onServiceConnected(ComponentName className, IBinder service) { bound = ((TerminalManager.TerminalBinder) service).getService(); - // TODO: update our green bulb icons by checking for existing - // bridges - // open up some test sessions - // try { - // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt", - // "screen", 100); - // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt", - // "screen", 100); - // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt", - // "screen", 100); - // } catch(Exception e) { - // e.printStackTrace(); - // } - + // TODO: update our green bulb icons by checking for existing bridges + } public void onServiceDisconnected(ComponentName className) { @@ -73,17 +62,29 @@ public class HostList extends Activity { public ListView list; public HostAdapter adapter; - public int COL_ID, COL_NICKNAME, COL_USERNAME, COL_HOSTNAME, COL_CONNECTED; + public int COL_ID, COL_NICKNAME, COL_USERNAME, COL_HOSTNAME, COL_CONNECTED, COL_PORT; @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - setContentView(R.layout.act_frontpage); + public void onStart() { + super.onStart(); // start the terminal manager service this.startService(new Intent(this, TerminalManager.class)); - this.bindService(new Intent(this, TerminalManager.class), connection, - Context.BIND_AUTO_CREATE); + this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + + } + + @Override + public void onStop() { + super.onStop(); + this.unbindService(connection); + + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.act_frontpage); // connect with hosts database and populate list this.hostdb = new HostDatabase(this); @@ -96,17 +97,23 @@ public class HostList extends Activity { this.COL_NICKNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME); this.COL_USERNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_USERNAME); this.COL_HOSTNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_HOSTNAME); + this.COL_PORT = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_PORT); this.COL_CONNECTED = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_LASTCONNECT); this.list.setOnItemClickListener(new OnItemClickListener() { - public void onItemClick(AdapterView<?> parent, View view, - int position, long id) { + public synchronized void onItemClick(AdapterView<?> parent, View view, int position, long id) { // launch off to console details - // TODO: package information about connection selected - HostList.this.startActivity(new Intent(HostList.this, - Console.class)); + Cursor c = (Cursor)parent.getAdapter().getItem(position); + String username = c.getString(COL_USERNAME); + String hostname = c.getString(COL_HOSTNAME); + int port = c.getInt(COL_PORT); + String nickname = c.getString(COL_NICKNAME); + + Intent intent = new Intent(HostList.this, Console.class); + intent.setData(Uri.parse(String.format("ssh://%s@%s:%s/#%s", username, hostname, port, nickname))); + HostList.this.startActivity(intent); } @@ -115,8 +122,7 @@ public class HostList extends Activity { this.registerForContextMenu(this.list); final Pattern hostmask = Pattern.compile(".+@.+(:\\d+)?"); - final TextView text = (TextView) this - .findViewById(R.id.front_quickconnect); + final TextView text = (TextView) this.findViewById(R.id.front_quickconnect); text.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { @@ -178,7 +184,7 @@ public class HostList extends Activity { add.setIcon(android.R.drawable.ic_menu_add); add.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - return false; + return true; } }); @@ -188,7 +194,7 @@ public class HostList extends Activity { public boolean onMenuItemClick(MenuItem item) { sortedByColor = true; updateCursor(); - return false; + return true; } }); @@ -198,7 +204,7 @@ public class HostList extends Activity { public boolean onMenuItemClick(MenuItem item) { sortedByColor = false; updateCursor(); - return false; + return true; } }); @@ -207,13 +213,31 @@ public class HostList extends Activity { MenuItem settings = menu.add(0, 0, Menu.NONE, "Settings"); settings.setIcon(android.R.drawable.ic_menu_preferences); + settings.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + HostList.this.startActivity(new Intent(HostList.this, SettingsActivity.class)); + return true; + } + }); MenuItem about = menu.add(0, 0, Menu.NONE, "About"); about.setIcon(android.R.drawable.ic_menu_help); + about.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + HostList.this.startActivity(new Intent(HostList.this, AboutActivity.class)); + return true; + } + }); return true; } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + this.updateCursor(); + } + + public final static int REQUEST_EDIT = 1; @Override public void onCreateContextMenu(ContextMenu menu, View v, @@ -242,7 +266,7 @@ public class HostList extends Activity { public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(HostList.this, HostEditor.class); intent.putExtra(Intent.EXTRA_TITLE, id); - HostList.this.startActivity(intent); + HostList.this.startActivityForResult(intent, REQUEST_EDIT); return false; } }); diff --git a/src/org/connectbot/R.java b/src/org/connectbot/R.java index b1a337f..027ba6c 100644 --- a/src/org/connectbot/R.java +++ b/src/org/connectbot/R.java @@ -70,17 +70,18 @@ public final class R { } public static final class layout { public static final int about_dialog=0x7f030000; - public static final int act_console=0x7f030001; - public static final int act_frontpage=0x7f030002; - public static final int act_hosteditor=0x7f030003; - public static final int host_editor=0x7f030004; - public static final int item_host=0x7f030005; - public static final int item_terminal=0x7f030006; - public static final int main=0x7f030007; - public static final int message_dialog=0x7f030008; - public static final int password_dialog=0x7f030009; - public static final int pubkey=0x7f03000a; - public static final int secure_shell=0x7f03000b; + public static final int act_about=0x7f030001; + public static final int act_console=0x7f030002; + public static final int act_frontpage=0x7f030003; + public static final int act_hosteditor=0x7f030004; + public static final int host_editor=0x7f030005; + public static final int item_host=0x7f030006; + public static final int item_terminal=0x7f030007; + public static final int main=0x7f030008; + public static final int message_dialog=0x7f030009; + public static final int password_dialog=0x7f03000a; + public static final int pubkey=0x7f03000b; + public static final int secure_shell=0x7f03000c; } public static final class string { public static final int alert_disconnect_msg=0x7f070014; @@ -109,5 +110,6 @@ public final class R { } public static final class xml { public static final int host_prefs=0x7f050000; + public static final int preferences=0x7f050001; } } diff --git a/src/org/connectbot/SettingsActivity.java b/src/org/connectbot/SettingsActivity.java new file mode 100644 index 0000000..2036958 --- /dev/null +++ b/src/org/connectbot/SettingsActivity.java @@ -0,0 +1,16 @@ +package org.connectbot; + +import android.os.Bundle; +import android.preference.PreferenceActivity; + +public class SettingsActivity extends PreferenceActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + + } + +} + diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index ed2424c..cadaf58 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.theb.ssh.InteractiveHostKeyVerifier; import org.theb.ssh.JTATerminalView; import android.graphics.Bitmap; @@ -21,8 +22,12 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnKeyListener; +import com.trilead.ssh2.InteractiveCallback; +import com.trilead.ssh2.KnownHosts; +import com.trilead.ssh2.ServerHostKeyVerifier; import com.trilead.ssh2.Session; import com.trilead.ssh2.Connection; +import com.trilead.ssh2.signature.RSAPublicKey; import de.mud.terminal.VDUBuffer; import de.mud.terminal.VDUDisplay; @@ -33,72 +38,170 @@ import de.mud.terminal.vt320; public class TerminalBridge implements VDUDisplay, OnKeyListener { public final Connection connection; - public final Session session; - public final String overlay; + public final String nickname, username; + public final Paint defaultPaint; + public Session session; public final static int TERM_WIDTH_CHARS = 80, TERM_HEIGHT_CHARS = 24, DEFAULT_FONT_SIZE = 10; public final static String ENCODING = "ASCII"; + public static final String AUTH_PUBLICKEY = "publickey", + AUTH_PASSWORD = "password"; - public final OutputStream stdin; - public final InputStream stdout; - - public final Paint defaultPaint; + public OutputStream stdin; + public InputStream stdout; - public final Thread relay; + public Thread relay; public View parent = null; public Bitmap bitmap = null; public Canvas canvas = new Canvas(); public VDUBuffer buffer = null; - public TerminalBridge(Connection connection, String overlay, String emulation, int scrollback) throws Exception { - // create a terminal bridge from an SSH connection over to a SurfaceHolder - // will open a new session and handle rendering to the Surface if present + + public class HostKeyVerifier implements ServerHostKeyVerifier { - try { - this.connection = connection; - this.session = connection.openSession(); - this.session.requestPTY(emulation, 0, 0, 0, 0, null); // previously tried vt100, xterm, but "screen" works the best - this.session.startShell(); - - this.overlay = overlay; + // hex routine adapted from + // http://forums.sun.com/thread.jspa?threadID=252591&messageID=2272668 + + private final char[] hex = "0123456789abcdef".toCharArray(); + + public String hexdump(byte[] buf) { + StringBuffer out = new StringBuffer(); + for(int i = 0; i < buf.length; i++) { + int value = buf[i] + 127; + out.append(":" + hex[(value>>>4)&0xf] + hex[value&0xf]); + } + return out.toString().substring(1); + } + + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { + // TODO: check against known key, prompt user if unknown or missing key - // grab stdin/out from newly formed session - this.stdin = this.session.getStdin(); - this.stdout = this.session.getStdout(); + KnownHosts hosts = new KnownHosts(); + switch(hosts.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey)) { + case KnownHosts.HOSTKEY_IS_OK: + return true; + + case KnownHosts.HOSTKEY_IS_NEW: + // prompt user + outputLine(String.format("The authenticity of host '%s' can't be established.", hostname)); + outputLine(String.format("RSA key fingerprint is %s", hosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey))); + outputLine("Are you sure you want to continue connecting (yes/no)? "); + return true; + + case KnownHosts.HOSTKEY_HAS_CHANGED: + return false; + + } - // create our default paint - this.defaultPaint = new Paint(); - this.defaultPaint.setAntiAlias(true); - this.defaultPaint.setTypeface(Typeface.MONOSPACE); - this.setFontSize(DEFAULT_FONT_SIZE); + return false; - // create terminal buffer and handle outgoing data - // this is probably status reply information - this.buffer = new vt320() { - public void write(byte[] b) { - try { - //Log.d("STDIN", new String(b)); - TerminalBridge.this.stdin.write(b); - } catch (IOException e) { - e.printStackTrace(); - } - } + } + + } + + public TerminalBridge(final String nickname, final String username, final String hostname, final int port) throws Exception { + // newer version of TerminalBridge that will dump all connection progress to the display + + this.nickname = nickname; + this.username = username; + + // create our default paint + this.defaultPaint = new Paint(); + this.defaultPaint.setAntiAlias(true); + this.defaultPaint.setTypeface(Typeface.MONOSPACE); + this.setFontSize(DEFAULT_FONT_SIZE); - public void sendTelnetCommand(byte cmd) { + for(int i = 0; i < color.length; i++) + this.darkerColor[i] = darken(color[i]); + + // create terminal buffer and handle outgoing data + // this is probably status reply information + this.buffer = new vt320() { + public void write(byte[] b) { + try { + TerminalBridge.this.stdin.write(b); + } catch (IOException e) { + e.printStackTrace(); } + } + + public void sendTelnetCommand(byte cmd) { + } + + public void setWindowSize(int c, int r) { + } + }; - public void setWindowSize(int c, int r) { + this.scrollback = scrollback; + this.buffer.setScreenSize(TERM_WIDTH_CHARS, TERM_HEIGHT_CHARS, true); + this.buffer.setBufferSize(scrollback); + this.buffer.setDisplay(this); + this.buffer.setCursorPosition(0, 0); + + // try opening ssh connection + this.outputLine(String.format("Connecting to %s:%d", hostname, port)); + this.connection = new Connection(hostname, port); + + new Thread(new Runnable() { + + public void run() { + try { + connection.connect(new HostKeyVerifier()); + outputLine("Trying to authenticate"); + if(connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { + // show auth prompt in window + promptPassword(); + } + } catch (IOException e) { + e.printStackTrace(); } - }; - this.scrollback = scrollback; - this.buffer.setScreenSize(TERM_WIDTH_CHARS, TERM_HEIGHT_CHARS, true); - this.buffer.setBufferSize(scrollback); - this.buffer.setDisplay(this); + } + }).start(); + + } + + public void outputLine(String line) { + this.buffer.putString(0, this.buffer.getCursorRow(), line); + this.buffer.setCursorPosition(0, this.buffer.getCursorRow() + 1); + this.redraw(); + } + + public void promptPassword() { + this.outputLine("Password: "); + } + + public void tryPassword(String password) { + try { + // try authenticating with given password + Log.d(this.getClass().toString(), String.format("tryPassword(password=%s) and username=%s", password, username)); + if(this.connection.authenticateWithPassword(this.username, password)) { + finishConnection(); + return; + } + } catch (IOException e) { + e.printStackTrace(); + } + this.outputLine("Permission denied, please try again."); + this.promptPassword(); + } + + + public void finishConnection() { + + try { + this.session = connection.openSession(); + this.session.requestPTY("screen", 0, 0, 0, 0, null); // previously tried vt100, xterm, but "screen" works the best + this.session.startShell(); + + // grab stdin/out from newly formed session + this.stdin = this.session.getStdin(); + this.stdout = this.session.getStdout(); + // create thread to relay incoming connection data to buffer this.relay = new Thread(new Runnable() { public void run() { @@ -120,25 +223,31 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { } }); this.relay.start(); - - } catch(Exception e) { - throw e; + + // force font-size to make sure we resizePTY as needed + this.setFontSize(this.fontSize); + + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); } - for(int i = 0; i < color.length; i++) - this.darkerColor[i] = darken(color[i]); - } public void dispose() { - this.session.close(); + if(this.session != null) + this.session.close(); this.connection.close(); } - KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); + public KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); + + StringBuffer collected = new StringBuffer(); public boolean onKey(View v, int keyCode, KeyEvent event) { // pass through any keystrokes to output stream + + Log.d(this.getClass().toString(), "onKey() code="+keyCode); if(event.getAction() == KeyEvent.ACTION_UP) return false; try { @@ -151,21 +260,42 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { return true; } - // print normal keys - if(keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE) { - int key = keymap.get(keyCode, event.getMetaState()); - this.stdin.write(key); - return true; - } + boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE); - // look for special chars - switch(keyCode) { - case KeyEvent.KEYCODE_DEL: stdin.write(0x08); return true; - case KeyEvent.KEYCODE_ENTER: ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', event.getMetaState()); return true; - case KeyEvent.KEYCODE_DPAD_LEFT: ((vt320)buffer).keyPressed(vt320.KEY_LEFT, ' ', event.getMetaState()); return true; - case KeyEvent.KEYCODE_DPAD_UP: ((vt320)buffer).keyPressed(vt320.KEY_UP, ' ', event.getMetaState()); return true; - case KeyEvent.KEYCODE_DPAD_DOWN: ((vt320)buffer).keyPressed(vt320.KEY_DOWN, ' ', event.getMetaState()); return true; - case KeyEvent.KEYCODE_DPAD_RIGHT: ((vt320)buffer).keyPressed(vt320.KEY_RIGHT, ' ', event.getMetaState()); return true; + if(this.session == null) { + // check to see if we are collecting password information + if(keyCode == KeyEvent.KEYCODE_ENTER) { + this.tryPassword(collected.toString()); + collected = new StringBuffer(); + return true; + } else if(printing) { + collected.appendCodePoint(keymap.get(keyCode, event.getMetaState())); + return true; + } else if(keyCode == KeyEvent.KEYCODE_DEL && collected.length() > 0) { + collected.deleteCharAt(collected.length() - 1); + return true; + } + + } else { + + // otherwise pass through to existing session + // print normal keys + if (printing) { + int key = keymap.get(keyCode, event.getMetaState()); + this.stdin.write(key); + return true; + } + + // look for special chars + switch(keyCode) { + case KeyEvent.KEYCODE_DEL: stdin.write(0x08); return true; + case KeyEvent.KEYCODE_ENTER: ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', event.getMetaState()); return true; + case KeyEvent.KEYCODE_DPAD_LEFT: ((vt320)buffer).keyPressed(vt320.KEY_LEFT, ' ', event.getMetaState()); return true; + case KeyEvent.KEYCODE_DPAD_UP: ((vt320)buffer).keyPressed(vt320.KEY_UP, ' ', event.getMetaState()); return true; + case KeyEvent.KEYCODE_DPAD_DOWN: ((vt320)buffer).keyPressed(vt320.KEY_DOWN, ' ', event.getMetaState()); return true; + case KeyEvent.KEYCODE_DPAD_RIGHT: ((vt320)buffer).keyPressed(vt320.KEY_RIGHT, ' ', event.getMetaState()); return true; + } + } } catch (IOException e) { @@ -173,6 +303,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { } return false; } + public int charWidth = -1, charHeight = -1, @@ -227,7 +358,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener { try { buffer.setScreenSize(termWidth, termHeight, true); - session.resizePTY(termWidth, termHeight); + if(session != null) + session.resizePTY(termWidth, termHeight); } catch(Exception e) { e.printStackTrace(); } diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index 72203ba..0f4da75 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -9,6 +9,7 @@ import com.trilead.ssh2.Connection; import android.app.Service; import android.content.Intent; +import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.util.Log; @@ -31,37 +32,58 @@ public class TerminalManager extends Service { bridge.dispose(); } - public void openConnection(Connection conn, String nickname, String emulation, int scrollback) throws Exception { - try { - TerminalBridge bridge = new TerminalBridge(conn, nickname, emulation, scrollback); - this.bridges.add(bridge); - } catch (Exception e) { - throw e; - } +// public void openConnection(Connection conn, String nickname, String emulation, int scrollback) throws Exception { +// try { +// TerminalBridge bridge = new TerminalBridge(conn, nickname, emulation, scrollback); +// this.bridges.add(bridge); +// } catch (Exception e) { +// throw e; +// } +// } +// +// public void openConnection(String nickname, String host, int port, String user, String pass, String emulation, int scrollback) throws Exception { +// try { +// Connection conn = new Connection(host, port); +// conn.connect(new InteractiveHostKeyVerifier()); +// if(conn.isAuthMethodAvailable(user, "password")) { +// conn.authenticateWithPassword(user, pass); +// } +// TerminalBridge bridge = new TerminalBridge(conn, nickname, emulation, scrollback); +// this.bridges.add(bridge); +// } catch (Exception e) { +// throw e; +// } +// } + + public void openConnection(String nickname, String hostname, String username, int port) throws Exception { + TerminalBridge bridge = new TerminalBridge(nickname, username, hostname, port); + this.bridges.add(bridge); } - - public void openConnection(String nickname, String host, int port, String user, String pass, String emulation, int scrollback) throws Exception { - try { - Connection conn = new Connection(host, port); - conn.connect(new InteractiveHostKeyVerifier()); - if(conn.isAuthMethodAvailable(user, "password")) { - conn.authenticateWithPassword(user, pass); - } - TerminalBridge bridge = new TerminalBridge(conn, nickname, emulation, scrollback); - this.bridges.add(bridge); - } catch (Exception e) { - throw e; - } + + public void openConnection(Uri uri) throws Exception { + String nickname = uri.getFragment(); + String username = uri.getUserInfo(); + String hostname = uri.getHost(); + int port = uri.getPort(); + + TerminalBridge bridge = new TerminalBridge(nickname, username, hostname, port); + this.bridges.add(bridge); } public TerminalBridge findBridge(String nickname) { // find the first active bridge with given nickname for(TerminalBridge bridge : bridges) { - if(bridge.overlay.equals(nickname)) + if(bridge.nickname.equals(nickname)) return bridge; } return null; } + + public void disconnect(TerminalBridge bridge) { + bridge.dispose(); + this.bridges.remove(bridge); + + } public class TerminalBinder extends Binder { diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index bbc27f9..586246b 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -13,7 +13,7 @@ import android.database.sqlite.SQLiteOpenHelper; public class HostDatabase extends SQLiteOpenHelper { public final static String DB_NAME = "hosts"; - public final static int DB_VERSION = 4; + public final static int DB_VERSION = 5; public final static String TABLE_HOSTS = "hosts"; public final static String FIELD_HOST_NICKNAME = "nickname"; @@ -23,6 +23,7 @@ public class HostDatabase extends SQLiteOpenHelper { public final static String FIELD_HOST_HOSTKEY = "hostkey"; public final static String FIELD_HOST_LASTCONNECT = "lastconnect"; public final static String FIELD_HOST_COLOR = "color"; + public final static String FIELD_HOST_USEKEYS = "usekeys"; public final static String TABLE_PRIVKEYS = "keys"; public final static String FIELD_KEY_NAME = "name"; @@ -46,7 +47,8 @@ public class HostDatabase extends SQLiteOpenHelper { + FIELD_HOST_PORT + " INTEGER, " + FIELD_HOST_HOSTKEY + " TEXT, " + FIELD_HOST_LASTCONNECT + " INTEGER, " - + FIELD_HOST_COLOR + " TEXT)"); + + FIELD_HOST_COLOR + " TEXT, " + + FIELD_HOST_USEKEYS + " TEXT)"); db.execSQL("CREATE TABLE " + TABLE_PRIVKEYS + " (_id INTEGER PRIMARY KEY, " @@ -78,6 +80,7 @@ public class HostDatabase extends SQLiteOpenHelper { values.put(FIELD_HOST_HOSTNAME, hostname); values.put(FIELD_HOST_PORT, port); values.put(FIELD_HOST_LASTCONNECT, Integer.MAX_VALUE); + values.put(FIELD_HOST_USEKEYS, Boolean.toString(true)); if(color != null) values.put(FIELD_HOST_COLOR, color); diff --git a/src/org/theb/ssh/HostsList.java b/src/org/theb/ssh/HostsList.java index 5760f46..eb0bc53 100644 --- a/src/org/theb/ssh/HostsList.java +++ b/src/org/theb/ssh/HostsList.java @@ -344,7 +344,7 @@ public class HostsList extends ListActivity { //Connection conn; //bound.openConnection(conn, nickname, emulation, scrollback); if (password != null) { - bound.openConnection(nickname, hostname, port, username, password, "screen", 100); + //bound.openConnection(nickname, hostname, port, username, password, "screen", 100); // open the console view and select this specific terminal Intent intent = new Intent(HostsList.this, Console.class); |