aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJeffrey Sharkey <jsharkey@jsharkey.org>2008-10-26 00:00:36 +0000
committerJeffrey Sharkey <jsharkey@jsharkey.org>2008-10-26 00:00:36 +0000
commit02b5d6cde18301bb5d1895896b18845c776c805d (patch)
tree4c91eb46ddf091efb243ef93ce841f17b17ef548 /src
parent2cecc0403f922721d6c19505b342935e9a29de14 (diff)
downloadconnectbot-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.java174
-rw-r--r--src/org/connectbot/R.java20
-rw-r--r--src/org/connectbot/service/PromptHelper.java102
-rw-r--r--src/org/connectbot/service/TerminalBridge.java207
-rw-r--r--src/org/connectbot/service/TerminalManager.java22
-rw-r--r--src/org/connectbot/util/HostDatabase.java65
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;
+ }
+
}