diff options
author | Jeffrey Sharkey <jsharkey@jsharkey.org> | 2008-10-26 00:00:36 +0000 |
---|---|---|
committer | Jeffrey Sharkey <jsharkey@jsharkey.org> | 2008-10-26 00:00:36 +0000 |
commit | 02b5d6cde18301bb5d1895896b18845c776c805d (patch) | |
tree | 4c91eb46ddf091efb243ef93ce841f17b17ef548 /src | |
parent | 2cecc0403f922721d6c19505b342935e9a29de14 (diff) | |
download | connectbot-02b5d6cde18301bb5d1895896b18845c776c805d.tar.gz connectbot-02b5d6cde18301bb5d1895896b18845c776c805d.tar.bz2 connectbot-02b5d6cde18301bb5d1895896b18845c776c805d.zip |
* refactored prompting gui component into PromptHelper, makes it easier now because it offers blocking methods to get strings
* also added boolean prompt support for hostkey yes/no from user
* added checking and storing of hostkeys into backend database, successfully tested
* created new icon using android style guide pdf, older icon is still here
Diffstat (limited to 'src')
-rw-r--r-- | src/org/connectbot/ConsoleActivity.java | 174 | ||||
-rw-r--r-- | src/org/connectbot/R.java | 20 | ||||
-rw-r--r-- | src/org/connectbot/service/PromptHelper.java | 102 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalBridge.java | 207 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalManager.java | 22 | ||||
-rw-r--r-- | src/org/connectbot/util/HostDatabase.java | 65 |
6 files changed, 433 insertions, 157 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index d63f5aa..b97669e 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -18,6 +18,7 @@ package org.connectbot; +import org.connectbot.service.PromptHelper; import org.connectbot.service.TerminalBridge; import org.connectbot.service.TerminalManager; @@ -31,6 +32,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.PowerManager; import android.text.ClipboardManager; import android.util.Log; import android.view.GestureDetector; @@ -43,10 +45,12 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.Window; import android.view.MenuItem.OnMenuItemClickListener; +import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.Button; import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; @@ -66,7 +70,7 @@ public class ConsoleActivity extends Activity { bound = ((TerminalManager.TerminalBinder) service).getService(); // let manager know about our event handling services - bound.parentHandler = parentHandler; + bound.disconnectHandler = disconnectHandler; Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size())); @@ -95,8 +99,8 @@ public class ConsoleActivity extends Activity { // create views for all bridges on this service for(TerminalBridge bridge : bound.bridges) { - // let them know about our password services - bridge.parentHandler = parentHandler; + // let them know about our prompt handler services + bridge.promptHelper.setHandler(promptHandler); // inflate each terminal view RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false); @@ -128,9 +132,9 @@ public class ConsoleActivity extends Activity { } public void onServiceDisconnected(ComponentName className) { - // remove all bridge views + // tell each bridge to forget about our prompt handler for(TerminalBridge bridge : bound.bridges) - bridge.parentHandler = null; + bridge.promptHelper.setHandler(null); flip.removeAllViews(); updateEmptyVisible(); @@ -145,63 +149,81 @@ public class ConsoleActivity extends Activity { return ((TerminalView)view).bridge.nickname; } - public final static int HANDLE_PROMPT = 1, HANDLE_DISCONNECT = 2; - - public Handler parentHandler = new Handler() { + public Handler promptHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // someone below us requested to display a prompt + updatePromptVisible(); + + } + }; + + public Handler disconnectHandler = new Handler() { @Override public void handleMessage(Message msg) { // someone below us requested to display a password dialog // they are sending nickname and requested TerminalBridge bridge = (TerminalBridge)msg.obj; - switch(msg.what) { - case HANDLE_PROMPT: - // someone is requesting a prompt field update - updatePromptVisible(); - break; - - case HANDLE_DISCONNECT: - // remove this bridge becase its been disconnected - Log.d(TAG, "Someone sending HANDLE_DISCONNECT to parentHandler"); - for(int i = 0; i < flip.getChildCount(); i++) { - View view = flip.getChildAt(i).findViewById(R.id.console_flip); - if(!(view instanceof TerminalView)) continue; - TerminalView terminal = (TerminalView)view; - if(terminal.bridge.equals(bridge)) { - // weve found the terminal to remove - // shift something into its place if currently visible - if(flip.getDisplayedChild() == i) { - shiftLeft(); - } - flip.removeViewAt(i); - updateEmptyVisible(); - break; + // remove this bridge becase its been disconnected + Log.d(TAG, "Someone sending HANDLE_DISCONNECT to parentHandler"); + for(int i = 0; i < flip.getChildCount(); i++) { + View view = flip.getChildAt(i).findViewById(R.id.console_flip); + if(!(view instanceof TerminalView)) continue; + TerminalView terminal = (TerminalView)view; + if(terminal.bridge.equals(bridge)) { + // weve found the terminal to remove + // shift something into its place if currently visible + if(flip.getDisplayedChild() == i) { + shiftLeft(); } + flip.removeViewAt(i); + updateEmptyVisible(); + break; } - break; - } } }; + protected void hideAllPrompts() { + this.stringPrompt.setVisibility(View.GONE); + this.booleanPrompt.setVisibility(View.GONE); + this.booleanYes.setVisibility(View.GONE); + this.booleanNo.setVisibility(View.GONE); + + } + + /** + * Show any prompts requested by the currently visible {@link TerminalView}. + */ protected void updatePromptVisible() { - // check if our currently-visible terminalbridge is requesting password services + // check if our currently-visible terminalbridge is requesting any prompt services View view = findCurrentView(R.id.console_flip); - boolean requested = false; - if(view instanceof TerminalView) - requested = ((TerminalView)view).bridge.promptRequested; + if(!(view instanceof TerminalView)) { + // we dont have an active view, so hide any prompts + this.hideAllPrompts(); + return; + } - // handle showing/hiding password field and transferring focus - if(requested) { - this.password.setVisibility(View.VISIBLE); - this.password.setText(""); - this.password.setHint(((TerminalView)view).bridge.promptHint); - this.password.requestFocus(); + PromptHelper prompt = ((TerminalView)view).bridge.promptHelper; + if(String.class.equals(prompt.promptRequested)) { + this.stringPrompt.setVisibility(View.VISIBLE); + this.stringPrompt.setText(""); + this.stringPrompt.setHint(prompt.promptHint); + this.stringPrompt.requestFocus(); + + } else if(Boolean.class.equals(prompt.promptRequested)) { + this.booleanPrompt.setVisibility(View.VISIBLE); + this.booleanPrompt.setText(prompt.promptHint); + this.booleanYes.setVisibility(View.VISIBLE); + this.booleanYes.requestFocus(); + this.booleanNo.setVisibility(View.VISIBLE); + } else { - this.password.setVisibility(View.GONE); - if(view != null) - view.requestFocus(); + this.hideAllPrompts(); + view.requestFocus(); + } } @@ -221,6 +243,8 @@ public class ConsoleActivity extends Activity { bound.defaultBridge = terminal.bridge; } + protected PowerManager.WakeLock wakelock; + @Override public void onStart() { super.onStart(); @@ -229,12 +253,21 @@ public class ConsoleActivity extends Activity { // when connected it will insert all views this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + // make sure we dont let the screen fall asleep + // this also keeps the wifi chipset from disconnecting us + if (this.wakelock != null) + wakelock.acquire(); + } @Override public void onStop() { super.onStop(); this.unbindService(connection); + + // allow the screen to dim and fall asleep + if (this.wakelock != null) + wakelock.release(); } @@ -244,10 +277,19 @@ public class ConsoleActivity extends Activity { return view.findViewById(id); } + protected PromptHelper getCurrentPromptHelper() { + View view = findCurrentView(R.id.console_flip); + if(!(view instanceof TerminalView)) return null; + return ((TerminalView)view).bridge.promptHelper; + } + protected Uri requested; protected ClipboardManager clipboard; - protected EditText password; + protected EditText stringPrompt; + protected TextView booleanPrompt; + protected Button booleanYes, booleanNo; + protected TextView empty; protected Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out; @@ -261,6 +303,9 @@ public class ConsoleActivity extends Activity { this.clipboard = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE); + PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE); + wakelock = manager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG); + // handle requested console from incoming intent this.requested = this.getIntent().getData(); @@ -269,26 +314,49 @@ public class ConsoleActivity extends Activity { this.flip = (ViewFlipper)this.findViewById(R.id.console_flip); this.empty = (TextView)this.findViewById(android.R.id.empty); - this.password = (EditText)this.findViewById(R.id.console_password); - this.password.setOnKeyListener(new OnKeyListener() { + this.stringPrompt = (EditText)this.findViewById(R.id.console_password); + this.stringPrompt.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_UP) return false; if(keyCode != KeyEvent.KEYCODE_ENTER) return false; // pass collected password down to current terminal - String value = password.getText().toString(); + String value = stringPrompt.getText().toString(); - View view = findCurrentView(R.id.console_flip); - if(!(view instanceof TerminalView)) return true; - ((TerminalView)view).bridge.incomingPassword(value); + PromptHelper helper = getCurrentPromptHelper(); + if(helper == null) return false; + helper.setResponse(value); // finally clear password for next user - password.setText(""); + stringPrompt.setText(""); + hideAllPrompts(); return true; } }); + this.booleanPrompt = (TextView)this.findViewById(R.id.console_prompt); + + this.booleanYes = (Button)this.findViewById(R.id.console_prompt_yes); + this.booleanYes.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + PromptHelper helper = getCurrentPromptHelper(); + if(helper == null) return; + helper.setResponse(Boolean.TRUE); + hideAllPrompts(); + } + }); + + this.booleanNo = (Button)this.findViewById(R.id.console_prompt_no); + this.booleanNo.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + PromptHelper helper = getCurrentPromptHelper(); + if(helper == null) return; + helper.setResponse(Boolean.FALSE); + hideAllPrompts(); + } + }); + // preload animations for terminal switching this.slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); this.slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); diff --git a/src/org/connectbot/R.java b/src/org/connectbot/R.java index 1f896c0..85662bb 100644 --- a/src/org/connectbot/R.java +++ b/src/org/connectbot/R.java @@ -28,23 +28,27 @@ public final class R { public static final int red=0x7f080002; } public static final class drawable { - public static final int blue=0x7f020005; + public static final int blue=0x7f020006; public static final int connected=0x7f020000; - public static final int even_stripe=0x7f020006; + public static final int even_stripe=0x7f020007; public static final int highlight_disabled_pressed=0x7f020001; public static final int ic_btn_back=0x7f020002; public static final int ic_btn_next=0x7f020003; public static final int icon=0x7f020004; - public static final int odd_stripe=0x7f020007; + public static final int icon_older=0x7f020005; + public static final int odd_stripe=0x7f020008; } public static final class id { - public static final int action_next=0x7f090005; - public static final int action_prev=0x7f090004; + public static final int action_next=0x7f090008; + public static final int action_prev=0x7f090007; public static final int console_flip=0x7f090000; public static final int console_password=0x7f090001; - public static final int front_quickconnect=0x7f090002; - public static final int terminal_overlay=0x7f090006; - public static final int wizard_flipper=0x7f090003; + public static final int console_prompt=0x7f090002; + public static final int console_prompt_no=0x7f090003; + public static final int console_prompt_yes=0x7f090004; + public static final int front_quickconnect=0x7f090005; + public static final int terminal_overlay=0x7f090009; + public static final int wizard_flipper=0x7f090006; } public static final class layout { public static final int act_console=0x7f030000; diff --git a/src/org/connectbot/service/PromptHelper.java b/src/org/connectbot/service/PromptHelper.java new file mode 100644 index 0000000..66f1591 --- /dev/null +++ b/src/org/connectbot/service/PromptHelper.java @@ -0,0 +1,102 @@ +package org.connectbot.service; + +import java.util.concurrent.Semaphore; + +import android.os.Handler; +import android.os.Message; + +/** + * Helps provide a relay for prompts and responses between a possible user + * interface and some underlying service. + * + * @author jsharkey + */ +public class PromptHelper { + + protected final Object tag; + + public PromptHelper(Object tag) { + this.tag = tag; + } + + protected Handler handler = null; + + /** + * Register a user interface handler, if available. + */ + public void setHandler(Handler handler) { + this.handler = handler; + } + + protected Semaphore promptResponse = new Semaphore(0); + + public String promptHint = null; + public Class promptRequested = null; + + protected Object response = null; + + /** + * Set an incoming value from an above user interface. Will automatically + * notify any waiting requests. + */ + public void setResponse(Object value) { + this.response = value; + this.promptResponse.release(); + } + + /** + * Return the internal response value just before erasing and returning it. + */ + protected Object popResponse() { + Object value = this.response; + this.response = null; + return value; + } + + + /** + * Request a prompt response from parent. This is a blocking call until user + * interface returns a value. + */ + public synchronized Object requestPrompt(String hint, Class type) throws Exception { + this.promptHint = hint; + this.promptRequested = type; + + // notify any parent watching for live events + if(handler != null) + Message.obtain(handler, -1, tag).sendToTarget(); + + // acquire lock until user passes back value + this.promptResponse.acquire(); + this.promptRequested = null; + return this.popResponse(); + } + + /** + * Request a string response from parent. This is a blocking call until user + * interface returns a value. + */ + public String requestStringPrompt(String hint) { + String value = null; + try { + value = (String)this.requestPrompt(hint, String.class); + } catch(Exception e) { + } + return value; + } + + /** + * Request a boolean response from parent. This is a blocking call until user + * interface returns a value. + */ + public Boolean requestBooleanPrompt(String hint) { + Boolean value = null; + try { + value = (Boolean)this.requestPrompt(hint, Boolean.class); + } catch(Exception e) { + } + return value; + } + + +} diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index 93aeb53..3fe73f0 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -25,6 +25,7 @@ import java.util.concurrent.Semaphore; import org.connectbot.ConsoleActivity; import org.connectbot.TerminalView; +import org.connectbot.util.HostDatabase; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -92,6 +93,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public final static int COLOR_BG_STD = 0; public final String nickname; + protected final HostDatabase hostdb; protected final String username; public String postlogin = null; @@ -117,18 +119,15 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private boolean ctrlPressed = false; private String currentMethod = null; - private Semaphore waitChallengeResponse; - private String currentChallengeResponse = null; public class HostKeyVerifier implements ServerHostKeyVerifier { 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 - // TODO: check to see what hostkey checking the trilead library offers + // read in all known hosts from hostdb + KnownHosts hosts = hostdb.getKnownHosts(); - KnownHosts hosts = new KnownHosts(); switch(hosts.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey)) { case KnownHosts.HOSTKEY_IS_OK: return true; @@ -137,11 +136,25 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // prompt user outputLine(String.format("The authenticity of host '%s' can't be established.", hostname)); outputLine(String.format("RSA key fingerprint is %s", KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey))); - outputLine("[For now we'll assume you accept this key, but tap Menu and Disconnect if not.]"); + //outputLine("[For now we'll assume you accept this key, but tap Menu and Disconnect if not.]"); //outputLine("Are you sure you want to continue connecting (yes/no)? "); - return true; + Boolean result = promptHelper.requestBooleanPrompt("Are you sure you want\nto continue connecting?"); + if(result == null) return false; + if(result.booleanValue()) { + // save this key in known database + hostdb.saveKnownHost(hostname, serverHostKeyAlgorithm, serverHostKey); + } + return result.booleanValue(); case KnownHosts.HOSTKEY_HAS_CHANGED: + outputLine("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + outputLine("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); + outputLine("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + outputLine("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); + outputLine("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); + outputLine("It is also possible that the RSA host key has just been changed."); + outputLine(String.format("RSA key fingerprint is %s", KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey))); + outputLine("Host key verification failed."); return false; } @@ -152,18 +165,24 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } + public PromptHelper promptHelper; + /** * Create new terminal bridge with following parameters. We will immediately * launch thread to start SSH connection and handle any hostkey verification * and password authentication. */ - public TerminalBridge(final String nickname, final String username, final String hostname, final int port, String emulation, int scrollback) throws Exception { + public TerminalBridge(final HostDatabase hostdb, final String nickname, final String username, final String hostname, final int port, String emulation, int scrollback) throws Exception { + this.hostdb = hostdb; this.nickname = nickname; this.username = username; this.emulation = emulation; this.scrollback = scrollback; + + // create prompt helper to relay password and hostkey requests up to gui + this.promptHelper = new PromptHelper(this); // create our default paint this.defaultPaint = new Paint(); @@ -199,7 +218,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.buffer.setDisplay(this); this.buffer.setCursorPosition(0, 0); - // try opening ssh connection + // prepare the ssh connection for opening + // we perform the actual connection later in startConnection() this.outputLine(String.format("Connecting to %s:%d", hostname, port)); this.connection = new Connection(hostname, port); this.connection.addConnectionMonitor(this); @@ -209,41 +229,76 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal /** * Spawn thread to open connection and start login process. */ - public void startLogin() { + public void startConnection() { new Thread(new Runnable() { public void run() { try { connection.connect(new HostKeyVerifier()); - outputLine("Trying to authenticate"); - - // TODO: insert publickey auth check here - if(connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { - currentMethod = AUTH_PASSWORD; - requestPromptVisible(true, "Password"); - - } else if (connection.isAuthMethodAvailable(username, AUTH_KEYBOARDINTERACTIVE)) { - currentMethod = AUTH_KEYBOARDINTERACTIVE; - // this auth method will talk with us using InteractiveCallback interface - // it blocks until that auth is finished, which means - if (connection.authenticateWithKeyboardInteractive(username, TerminalBridge.this)) { - requestPromptVisible(false, null); - finishConnection(); - } - - } else { - outputLine("[Your host doesn't support 'password' or 'keyboard-interactive' authentication.]"); + // enter a loop to keep trying until authentication + while(!connection.isAuthenticationComplete()) { + handleAuthentication(); + // sleep to make sure we dont kill system + Thread.sleep(1000); } - } catch (IOException e) { + } catch(Exception e) { Log.e(TAG, "Problem in SSH connection thread", e); } } }).start(); + } + + public void handleAuthentication() { + outputLine("Trying to authenticate"); + + try { + + // TODO: insert publickey auth check here + + if(connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { + outputLine("Attempting 'password' authentication"); + String password = promptHelper.requestStringPrompt("Password"); + if(connection.authenticateWithPassword(username, password)) { + finishConnection(); + } else { + outputLine("Authentication method 'password' failed"); + } + + } else if(connection.isAuthMethodAvailable(username, AUTH_KEYBOARDINTERACTIVE)) { + // this auth method will talk with us using InteractiveCallback interface + // it blocks until authentication finishes + outputLine("Attempting 'keyboard-interactive' authentication"); + if(connection.authenticateWithKeyboardInteractive(username, TerminalBridge.this)) { + finishConnection(); + } else { + outputLine("Authentication method 'keyboard-interactive' failed"); + } + + } else { + outputLine("[Your host doesn't support 'password' or 'keyboard-interactive' authentication.]"); + + } + } catch(Exception e) { + Log.e(TAG, "Problem during handleAuthentication()", e); + } } /** + * Handle challenges from keyboard-interactive authentication mode. + */ + public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws Exception { + String[] responses = new String[numPrompts]; + for(int i = 0; i < numPrompts; i++) { + // request response from user for each prompt + responses[i] = promptHelper.requestStringPrompt(prompt[i]); + } + return responses; + } + + + /** * Convenience method for writing a line into the underlying MUD buffer. */ protected void outputLine(String line) { @@ -252,42 +307,42 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.redraw(); } - public Handler parentHandler = null; - - public boolean promptRequested = false; - public String promptHint = null; - - protected void requestPromptVisible(boolean visible, String hint) { - this.promptRequested = visible; - this.promptHint = hint; - - // pass notification up to any attached gui - if(this.parentHandler != null) - Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_PROMPT, this).sendToTarget(); - } - - /** - * Attempt to try password authentication using given string. - */ - public void incomingPassword(String password) { - try { - if (currentMethod == AUTH_PASSWORD) { - Log.d(TAG, "Attempting to try password authentication"); - if (this.connection.authenticateWithPassword(this.username, password)) { - requestPromptVisible(false, null); - finishConnection(); - return; - } - } else if (currentMethod == AUTH_KEYBOARDINTERACTIVE) { - Log.d(TAG, "Attempting to try keyboard-interactive authentication"); - currentChallengeResponse = password; - waitChallengeResponse.release(); - } - } catch (IOException e) { - Log.e(TAG, "Problem while trying to authenticate with password", e); - } - this.outputLine("Permission denied, please try again."); - } +// public Handler parentHandler = null; +// +// public boolean promptRequested = false; +// public String promptHint = null; +// +// protected void requestPromptVisible(boolean visible, String hint) { +// this.promptRequested = visible; +// this.promptHint = hint; +// +// // pass notification up to any attached gui +// if(this.parentHandler != null) +// Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_PROMPT, this).sendToTarget(); +// } + +// /** +// * Attempt to try password authentication using given string. +// */ +// public void incomingPassword(String password) { +// try { +// if (currentMethod == AUTH_PASSWORD) { +// Log.d(TAG, "Attempting to try password authentication"); +// if (this.connection.authenticateWithPassword(this.username, password)) { +// requestPromptVisible(false, null); +// finishConnection(); +// return; +// } +// } else if (currentMethod == AUTH_KEYBOARDINTERACTIVE) { +// Log.d(TAG, "Attempting to try keyboard-interactive authentication"); +// currentChallengeResponse = password; +// waitChallengeResponse.release(); +// } +// } catch (IOException e) { +// Log.e(TAG, "Problem while trying to authenticate with password", e); +// } +// this.outputLine("Permission denied, please try again."); +// } /** * Inject a specific string into this terminal. Used for post-login strings @@ -384,11 +439,11 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); - /** - * Buffer of collected characters, for example when prompted for password or - * accepting a hostkey. - */ - //protected StringBuffer collected = new StringBuffer(); +// /** +// * Buffer of collected characters, for example when prompted for password or +// * accepting a hostkey. +// */ +// //protected StringBuffer collected = new StringBuffer(); /** * Handle onKey() events coming down from a {@link TerminalView} above us. @@ -668,18 +723,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.disconnect(); } - public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws Exception { - String[] responses = new String[numPrompts]; - waitChallengeResponse = new Semaphore(0); - - for (int i = 0; i < numPrompts; i++) { - requestPromptVisible(true, prompt[i]); - waitChallengeResponse.acquire(); - responses[i] = currentChallengeResponse; - } - - return responses; - } } diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index bcc9dde..de98eda 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -52,6 +52,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public List<String> disconnected = new LinkedList<String>(); + protected HostDatabase hostdb; protected SharedPreferences prefs; protected String pref_emulation, pref_scrollback; @@ -61,6 +62,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen this.prefs = PreferenceManager.getDefaultSharedPreferences(this); this.pref_emulation = this.getResources().getString(R.string.pref_emulation); this.pref_scrollback = this.getResources().getString(R.string.pref_scrollback); + + this.hostdb = new HostDatabase(this); + } @Override @@ -71,6 +75,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen for(TerminalBridge bridge : bridges) bridge.disconnect(); + if(this.hostdb != null) + this.hostdb.close(); + } /** @@ -90,15 +97,12 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } // find the post-connection string for this host - HostDatabase hostdb = new HostDatabase(this); String postlogin = hostdb.getPostLogin(nickname); - hostdb.close(); - - TerminalBridge bridge = new TerminalBridge(nickname, username, hostname, port, emulation, scrollback); + TerminalBridge bridge = new TerminalBridge(hostdb, nickname, username, hostname, port, emulation, scrollback); bridge.disconnectListener = this; bridge.postlogin = postlogin; - bridge.startLogin(); + bridge.startConnection(); this.bridges.add(bridge); // also update database with new connected time @@ -124,9 +128,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen * to {@link HostDatabase}. */ protected void touchHost(String nickname) { - HostDatabase hostdb = new HostDatabase(this); hostdb.touchHost(nickname); - hostdb.close(); } /** @@ -141,7 +143,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen return null; } - public Handler parentHandler = null; + public Handler disconnectHandler = null; /** * Force disconnection of this {@link TerminalBridge} and remove it from our @@ -161,8 +163,8 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen this.disconnected.add(bridge.nickname); // pass notification back up to gui - if(this.parentHandler != null) - Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_DISCONNECT, bridge).sendToTarget(); + if(this.disconnectHandler != null) + Message.obtain(this.disconnectHandler, -1, bridge).sendToTarget(); } diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index 0e4a30c..e203060 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -18,11 +18,14 @@ package org.connectbot.util; +import com.trilead.ssh2.KnownHosts; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; /** * Contains information about various SSH hosts, include public hostkey if known @@ -32,14 +35,17 @@ import android.database.sqlite.SQLiteOpenHelper; */ public class HostDatabase extends SQLiteOpenHelper { + public final static String TAG = HostDatabase.class.toString(); + public final static String DB_NAME = "hosts"; - public final static int DB_VERSION = 9; + public final static int DB_VERSION = 10; public final static String TABLE_HOSTS = "hosts"; public final static String FIELD_HOST_NICKNAME = "nickname"; public final static String FIELD_HOST_USERNAME = "username"; public final static String FIELD_HOST_HOSTNAME = "hostname"; public final static String FIELD_HOST_PORT = "port"; + public final static String FIELD_HOST_HOSTKEYALGO = "hostkeyalgo"; public final static String FIELD_HOST_HOSTKEY = "hostkey"; public final static String FIELD_HOST_LASTCONNECT = "lastconnect"; public final static String FIELD_HOST_COLOR = "color"; @@ -63,15 +69,15 @@ public class HostDatabase extends SQLiteOpenHelper { + FIELD_HOST_USERNAME + " TEXT, " + FIELD_HOST_HOSTNAME + " TEXT, " + FIELD_HOST_PORT + " INTEGER, " - + FIELD_HOST_HOSTKEY + " TEXT, " + + FIELD_HOST_HOSTKEYALGO + " TEXT, " + + FIELD_HOST_HOSTKEY + " BLOB, " + FIELD_HOST_LASTCONNECT + " INTEGER, " + FIELD_HOST_COLOR + " TEXT, " + FIELD_HOST_USEKEYS + " TEXT, " + FIELD_HOST_POSTLOGIN + ")"); // insert a few sample hosts, none of which probably connect - this.createHost(db, "connectbot@bravo", "connectbot", "192.168.254.230", 22, null); - this.createHost(db, "root@google.com", "root", "google.com", 22, null); + this.createHost(db, "connectbot@bravo", "connectbot", "192.168.254.230", 22, COLOR_GRAY); this.createHost(db, "cron@server.example.com", "cron", "server.example.com", 22, COLOR_BLUE); this.createHost(db, "backup@example.net", "backup", "example.net", 22, COLOR_BLUE); @@ -168,5 +174,56 @@ public class HostDatabase extends SQLiteOpenHelper { } + /** + * Record the given hostkey into database under this nickname. + */ + public void saveKnownHost(String hostname, String hostkeyalgo, byte[] hostkey) { + + SQLiteDatabase db = this.getReadableDatabase(); + + ContentValues values = new ContentValues(); + values.put(FIELD_HOST_HOSTKEYALGO, hostkeyalgo); + values.put(FIELD_HOST_HOSTKEY, hostkey); + + db.update(TABLE_HOSTS, values, FIELD_HOST_HOSTNAME + " = ?", new String[] { hostname }); + Log.d(TAG, String.format("Finished saving hostkey information for '%s'", hostname)); + + } + + /** + * Build list of known hosts for Trilead library. + */ + public KnownHosts getKnownHosts() { + KnownHosts known = new KnownHosts(); + + SQLiteDatabase db = this.getReadableDatabase(); + Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_HOSTNAME, + FIELD_HOST_HOSTKEYALGO, FIELD_HOST_HOSTKEY }, null, null, null, + null, null); + if(c == null) return null; + + int COL_HOSTNAME = c.getColumnIndexOrThrow(FIELD_HOST_HOSTNAME), + COL_HOSTKEYALGO = c.getColumnIndexOrThrow(FIELD_HOST_HOSTKEYALGO), + COL_HOSTKEY = c.getColumnIndexOrThrow(FIELD_HOST_HOSTKEY); + + while(c.moveToNext()) { + String hostname = c.getString(COL_HOSTNAME), + hostkeyalgo = c.getString(COL_HOSTKEYALGO); + byte[] hostkey = c.getBlob(COL_HOSTKEY); + + if(hostkeyalgo == null || hostkeyalgo.length() == 0) continue; + if(hostkey == null || hostkey.length == 0) continue; + + try { + known.addHostkey(new String[] { hostname }, hostkeyalgo, hostkey); + } catch(Exception e) { + Log.e(TAG, "Problem while adding a known host from database", e); + } + + } + + return known; + } + } |