aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/org/connectbot/ConsoleActivity.java77
-rw-r--r--src/org/connectbot/HostEditorActivity.java1
-rw-r--r--src/org/connectbot/HostListActivity.java71
-rw-r--r--src/org/connectbot/service/BridgeDisconnectedListener.java23
-rw-r--r--src/org/connectbot/service/TerminalBridge.java111
-rw-r--r--src/org/connectbot/service/TerminalManager.java35
-rw-r--r--src/org/connectbot/util/HostBinder.java25
-rw-r--r--src/org/connectbot/util/HostDatabase.java25
8 files changed, 283 insertions, 85 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index d85e085..d63f5aa 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -65,6 +65,9 @@ public class ConsoleActivity extends Activity {
public void onServiceConnected(ComponentName className, IBinder service) {
bound = ((TerminalManager.TerminalBinder) service).getService();
+ // let manager know about our event handling services
+ bound.parentHandler = parentHandler;
+
Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size()));
// clear out any existing bridges and record requested index
@@ -93,7 +96,7 @@ public class ConsoleActivity extends Activity {
for(TerminalBridge bridge : bound.bridges) {
// let them know about our password services
- bridge.passwordHandler = passwordHandler;
+ bridge.parentHandler = parentHandler;
// inflate each terminal view
RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
@@ -119,16 +122,18 @@ public class ConsoleActivity extends Activity {
// show the requested bridge if found, also fade out overlay
flip.setDisplayedChild(requestedIndex);
flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_out);
- updatePasswordVisible();
+ updatePromptVisible();
+ updateEmptyVisible();
}
public void onServiceDisconnected(ComponentName className) {
// remove all bridge views
for(TerminalBridge bridge : bound.bridges)
- bridge.passwordHandler = null;
+ bridge.parentHandler = null;
flip.removeAllViews();
+ updateEmptyVisible();
bound = null;
}
@@ -139,35 +144,59 @@ public class ConsoleActivity extends Activity {
if(!(view instanceof TerminalView)) return null;
return ((TerminalView)view).bridge.nickname;
}
-
- public Handler passwordHandler = new Handler() {
+ public final static int HANDLE_PROMPT = 1, HANDLE_DISCONNECT = 2;
+
+ public Handler parentHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// someone below us requested to display a password dialog
// they are sending nickname and requested
- String nickname = (String)msg.obj;
+ 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;
+ }
+ }
+ break;
- // if they are currently active, then obey request
- if(nickname.equals(getCurrentNickname())) {
- updatePasswordVisible();
}
-
+
}
};
- protected void updatePasswordVisible() {
+ protected void updatePromptVisible() {
// check if our currently-visible terminalbridge is requesting password services
View view = findCurrentView(R.id.console_flip);
boolean requested = false;
if(view instanceof TerminalView)
- requested = ((TerminalView)view).bridge.passwordRequested;
+ requested = ((TerminalView)view).bridge.promptRequested;
// handle showing/hiding password field and transferring focus
if(requested) {
this.password.setVisibility(View.VISIBLE);
this.password.setText("");
- this.password.setHint(((TerminalView)view).bridge.passwordHint);
+ this.password.setHint(((TerminalView)view).bridge.promptHint);
this.password.requestFocus();
} else {
this.password.setVisibility(View.GONE);
@@ -219,6 +248,7 @@ public class ConsoleActivity extends Activity {
protected ClipboardManager clipboard;
protected EditText password;
+ protected TextView empty;
protected Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out;
@@ -237,6 +267,7 @@ public class ConsoleActivity extends Activity {
this.inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
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() {
@@ -361,7 +392,13 @@ public class ConsoleActivity extends Activity {
});
}
+
+ protected void updateEmptyVisible() {
+ // update visibility of empty status message
+ this.empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE);
+ }
+
protected void shiftLeft() {
// keep current overlay from popping up again
View overlay = findCurrentView(R.id.terminal_overlay);
@@ -376,7 +413,7 @@ public class ConsoleActivity extends Activity {
overlay = findCurrentView(R.id.terminal_overlay);
if(overlay != null) overlay.startAnimation(fade_out);
- updatePasswordVisible();
+ updatePromptVisible();
}
@@ -395,7 +432,7 @@ public class ConsoleActivity extends Activity {
overlay = findCurrentView(R.id.terminal_overlay);
if(overlay != null) overlay.startAnimation(fade_out);
- updatePasswordVisible();
+ updatePromptVisible();
}
@@ -414,8 +451,9 @@ public class ConsoleActivity extends Activity {
if(view == null) return false;
TerminalView terminal = (TerminalView)view;
bound.disconnect(terminal.bridge);
- flip.removeView(flip.getCurrentView());
- shiftLeft();
+ // movement should now be happening over in onDisconnect() handler
+ //flip.removeView(flip.getCurrentView());
+ //shiftLeft();
return true;
}
});
@@ -438,10 +476,7 @@ public class ConsoleActivity extends Activity {
// 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);
- }
+ terminal.bridge.injectString(clip);
return true;
}
diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java
index 6d6e80a..86a517c 100644
--- a/src/org/connectbot/HostEditorActivity.java
+++ b/src/org/connectbot/HostEditorActivity.java
@@ -211,6 +211,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
public void updateSummaries() {
// for all text preferences, set hint as current database value
for(String key : this.pref.values.keySet()) {
+ if(key.equals("postlogin")) continue;
Preference pref = this.findPreference(key);
if(pref == null) continue;
if(pref instanceof CheckBoxPreference) continue;
diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java
index 6958569..9cf1063 100644
--- a/src/org/connectbot/HostListActivity.java
+++ b/src/org/connectbot/HostListActivity.java
@@ -20,15 +20,18 @@ package org.connectbot;
import java.util.regex.Pattern;
+import org.connectbot.service.TerminalBridge;
import org.connectbot.service.TerminalManager;
import org.connectbot.util.HostBinder;
import org.connectbot.util.HostDatabase;
import org.connectbot.util.UpdateHelper;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
@@ -40,6 +43,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.preference.PreferenceManager;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.Menu;
@@ -90,6 +94,8 @@ public class HostListActivity extends ListActivity {
// 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.updateCursor();
}
@@ -98,11 +104,16 @@ public class HostListActivity extends ListActivity {
super.onStop();
this.unbindService(connection);
+ if(this.hosts != null) {
+ this.hosts.close();
+ this.hosts = null;
+ }
+
}
- public final static String EULA = "eula";
+ public final static String PREF_EULA = "eula", PREF_SORTBYCOLOR = "sortByColor";
public final static int REQUEST_EDIT = 1;
public final static int REQUEST_EULA = 2;
@@ -117,7 +128,7 @@ public class HostListActivity extends ListActivity {
if(resultCode == Activity.RESULT_OK) {
// yay they agreed, so store that info
Editor edit = prefs.edit();
- edit.putBoolean(EULA, true);
+ edit.putBoolean(PREF_EULA, true);
edit.commit();
} else {
// user didnt agree, so close
@@ -134,7 +145,7 @@ public class HostListActivity extends ListActivity {
}
- protected boolean shortcut = false;
+ protected boolean makingShortcut = false;
@Override
public void onCreate(Bundle icicle) {
@@ -143,9 +154,9 @@ public class HostListActivity extends ListActivity {
// check for eula agreement
- this.prefs = this.getSharedPreferences(EULA, Context.MODE_PRIVATE);
+ this.prefs = PreferenceManager.getDefaultSharedPreferences(this);
- boolean agreed = prefs.getBoolean(EULA, false);
+ boolean agreed = prefs.getBoolean(PREF_EULA, false);
if(!agreed) {
this.startActivityForResult(new Intent(this, WizardActivity.class), REQUEST_EULA);
}
@@ -156,13 +167,15 @@ public class HostListActivity extends ListActivity {
- this.shortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction());
+ this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction());
// connect with hosts database and populate list
this.hostdb = new HostDatabase(this);
ListView list = this.getListView();
- this.updateCursor();
+ this.sortedByColor = prefs.getBoolean(PREF_SORTBYCOLOR, false);
+ this.updateCursor();
+
//this.list.setSelector(R.drawable.highlight_disabled_pressed);
this.COL_ID = hosts.getColumnIndexOrThrow("_id");
@@ -189,7 +202,7 @@ public class HostListActivity extends ListActivity {
contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- if (shortcut) {
+ if (makingShortcut) {
// create shortcut if requested
ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(HostListActivity.this, R.drawable.icon);
@@ -217,6 +230,7 @@ public class HostListActivity extends ListActivity {
final Pattern hostmask = Pattern.compile(".+@.+(:\\d+)?");
final TextView text = (TextView) this.findViewById(R.id.front_quickconnect);
+ text.setVisibility(makingShortcut ? View.GONE : View.VISIBLE);
text.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -259,11 +273,15 @@ public class HostListActivity extends ListActivity {
});
}
-
+
public MenuItem sortcolor, sortlast;
public boolean sortedByColor = false;
protected void updateCursor() {
+
+ Editor edit = prefs.edit();
+ edit.putBoolean(PREF_SORTBYCOLOR, sortedByColor);
+ edit.commit();
// refresh cursor because of possible sorting change
if(this.hosts != null)
@@ -294,9 +312,11 @@ public class HostListActivity extends ListActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
+
+ // dont offer menus when creating shortcut
+ if(makingShortcut) return true;
// add host, ssh keys, about
-
sortcolor = menu.add("Sort by color");
sortcolor.setIcon(android.R.drawable.ic_menu_share);
sortcolor.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -343,15 +363,19 @@ public class HostListActivity extends ListActivity {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
Cursor cursor = (Cursor) this.getListView().getItemAtPosition(info.position);
- menu.setHeaderTitle(cursor.getString(COL_NICKNAME));
+ final String nickname = cursor.getString(COL_NICKNAME);
+ menu.setHeaderTitle(nickname);
final int id = cursor.getInt(COL_ID);
// edit, disconnect, delete
- // TODO: change disconnect/connect based on status
MenuItem connect = menu.add("Disconnect");
+ final TerminalBridge bridge = bound.findBridge(nickname);
+ connect.setEnabled((bridge != null));
connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
- return false;
+ bound.disconnect(bridge);
+ updateHandler.sendEmptyMessage(-1);
+ return true;
}
});
@@ -361,16 +385,29 @@ public class HostListActivity extends ListActivity {
Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class);
intent.putExtra(Intent.EXTRA_TITLE, id);
HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT);
- return false;
+ return true;
}
});
MenuItem delete = menu.add("Delete host");
delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
- hostdb.deleteHost(id);
- updateHandler.sendEmptyMessage(-1);
- return false;
+ // prompt user to make sure they really want this
+ new AlertDialog.Builder(HostListActivity.this)
+ .setMessage(String.format("Are you sure you want to delete '%s'?", nickname))
+ .setPositiveButton("Yes, delete", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // make sure we disconnect
+ if(bridge != null)
+ bound.disconnect(bridge);
+
+ hostdb.deleteHost(id);
+ updateHandler.sendEmptyMessage(-1);
+ }
+ })
+ .setNegativeButton("Cancel", null).create().show();
+
+ return true;
}
});
diff --git a/src/org/connectbot/service/BridgeDisconnectedListener.java b/src/org/connectbot/service/BridgeDisconnectedListener.java
new file mode 100644
index 0000000..74ab0f2
--- /dev/null
+++ b/src/org/connectbot/service/BridgeDisconnectedListener.java
@@ -0,0 +1,23 @@
+/*
+ ConnectBot: simple, powerful, open-source SSH client for Android
+ Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.connectbot.service;
+
+public interface BridgeDisconnectedListener {
+ public void onDisconnected(TerminalBridge bridge);
+}
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index ab24528..93aeb53 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Semaphore;
+import org.connectbot.ConsoleActivity;
import org.connectbot.TerminalView;
import android.graphics.Bitmap;
@@ -41,6 +42,7 @@ import android.view.View;
import android.view.View.OnKeyListener;
import com.trilead.ssh2.Connection;
+import com.trilead.ssh2.ConnectionMonitor;
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.KnownHosts;
import com.trilead.ssh2.ServerHostKeyVerifier;
@@ -60,7 +62,7 @@ import de.mud.terminal.vt320;
* This class also provides SSH hostkey verification prompting, and password
* prompting.
*/
-public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCallback {
+public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCallback, ConnectionMonitor {
public final static String TAG = TerminalBridge.class.toString();
@@ -91,6 +93,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
public final String nickname;
protected final String username;
+ public String postlogin = null;
protected final Connection connection;
protected Session session;
@@ -199,34 +202,43 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
// try opening ssh connection
this.outputLine(String.format("Connecting to %s:%d", hostname, port));
this.connection = new Connection(hostname, port);
+ this.connection.addConnectionMonitor(this);
+ }
+
+ /**
+ * Spawn thread to open connection and start login process.
+ */
+ public void startLogin() {
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;
- // show auth prompt in window
- requestPasswordVisible(true, "Password");
- //promptPassword();
+ 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)) {
- TerminalBridge.this.buffer.deleteArea(0, 0, TerminalBridge.this.buffer.getColumns(), TerminalBridge.this.buffer.getRows());
- requestPasswordVisible(false, null);
+ requestPromptVisible(false, null);
finishConnection();
}
+
} else {
- outputLine("Looks like your host doesn't support 'password' authentication.");
- outputLine("Other auth methods, such as interactive and publickey, are still being written.");
+ outputLine("[Your host doesn't support 'password' or 'keyboard-interactive' authentication.]");
+
}
} catch (IOException e) {
Log.e(TAG, "Problem in SSH connection thread", e);
}
}
-
}).start();
}
@@ -240,21 +252,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
this.redraw();
}
-// protected void promptPassword() {
-// this.outputLine("Password: ");
-// }
+ public Handler parentHandler = null;
- public boolean passwordRequested = false;
- public Handler passwordHandler = null;
- public String passwordHint = null;
+ public boolean promptRequested = false;
+ public String promptHint = null;
- protected void requestPasswordVisible(boolean visible, String hint) {
- this.passwordRequested = visible;
- this.passwordHint = hint;
+ protected void requestPromptVisible(boolean visible, String hint) {
+ this.promptRequested = visible;
+ this.promptHint = hint;
// pass notification up to any attached gui
- if(this.passwordHandler != null)
- Message.obtain(this.passwordHandler, -1, this.nickname).sendToTarget();
+ if(this.parentHandler != null)
+ Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_PROMPT, this).sendToTarget();
}
/**
@@ -263,11 +272,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
public void incomingPassword(String password) {
try {
if (currentMethod == AUTH_PASSWORD) {
- // try authenticating with given password
Log.d(TAG, "Attempting to try password authentication");
if (this.connection.authenticateWithPassword(this.username, password)) {
- this.buffer.deleteArea(0, 0, this.buffer.getColumns(), this.buffer.getRows());
- requestPasswordVisible(false, null);
+ requestPromptVisible(false, null);
finishConnection();
return;
}
@@ -280,7 +287,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
Log.e(TAG, "Problem while trying to authenticate with password", e);
}
this.outputLine("Permission denied, please try again.");
-// this.promptPassword();
+ }
+
+ /**
+ * Inject a specific string into this terminal. Used for post-login strings
+ * and pasting clipboard.
+ */
+ public void injectString(String string) {
+ if(string == null || string.length() == 0) return;
+ KeyEvent[] events = keymap.getEvents(string.toCharArray());
+ for(KeyEvent event : events) {
+ this.onKey(null, event.getKeyCode(), event);
+ }
}
/**
@@ -291,6 +309,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
try {
this.session = connection.openSession();
+ buffer.deleteArea(0, 0, TerminalBridge.this.buffer.getColumns(), TerminalBridge.this.buffer.getRows());
// previously tried vt100 and xterm for emulation modes
// "screen" works the best for color and escape codes
@@ -326,6 +345,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
// force font-size to make sure we resizePTY as needed
this.setFontSize(this.fontSize);
+
+ // finally send any post-login string, if requested
+ this.injectString(postlogin);
} catch (IOException e1) {
Log.e(TAG, "Problem while trying to create PTY in finishConnection()", e1);
@@ -333,10 +355,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
}
+ protected BridgeDisconnectedListener disconnectListener = null;
+
+ public void setOnDisconnectedListener(BridgeDisconnectedListener disconnectListener) {
+ this.disconnectListener = disconnectListener;
+ }
+
/**
* Force disconnection of this terminal bridge.
*/
- public void dispose() {
+ public void disconnect() {
// disconnection request hangs if we havent really connected to a host yet
// temporary fix is to just spawn disconnection into a thread
new Thread(new Runnable() {
@@ -346,6 +374,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
connection.close();
}
}).start();
+
+ // pass notification back up to terminal manager
+ // the manager will do any gui notification if applicable
+ if(this.disconnectListener != null)
+ this.disconnectListener.onDisconnected(this);
+
}
public KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
@@ -484,6 +518,14 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
int width = parent.getWidth();
int height = parent.getHeight();
+ // recalculate buffer size
+ int termWidth = width / charWidth;
+ int termHeight = height / charHeight;
+
+ // convert our height/width to integral values
+ width = termWidth * charWidth;
+ height = termHeight * charHeight;
+
// reallocate new bitmap if needed
boolean newBitmap = (this.bitmap == null);
if(this.bitmap != null)
@@ -497,10 +539,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
this.defaultPaint.setColor(Color.BLACK);
this.canvas.drawRect(0, 0, width, height, this.defaultPaint);
- // recalculate buffer size and update buffer
- int termWidth = width / charWidth;
- int termHeight = height / charHeight;
-
try {
// request a terminal pty resize
buffer.setScreenSize(termWidth, termHeight, true);
@@ -624,14 +662,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
public void updateScrollBar() {
}
- public String[] replyToChallenge(String name, String instruction,
- int numPrompts, String[] prompt, boolean[] echo) throws Exception {
+ public void connectionLost(Throwable reason) {
+ // weve lost our ssh connection, so pass along to manager and gui
+ Log.e(TAG, "Somehow our underlying SSH socket died", reason);
+ 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++) {
- requestPasswordVisible(true, prompt[i]);
+ requestPromptVisible(true, prompt[i]);
waitChallengeResponse.acquire();
responses[i] = currentChallengeResponse;
}
@@ -639,6 +681,5 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
return responses;
}
-
}
diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java
index 2c5e6c2..bcc9dde 100644
--- a/src/org/connectbot/service/TerminalManager.java
+++ b/src/org/connectbot/service/TerminalManager.java
@@ -21,6 +21,7 @@ package org.connectbot.service;
import java.util.LinkedList;
import java.util.List;
+import org.connectbot.ConsoleActivity;
import org.connectbot.R;
import org.connectbot.util.HostDatabase;
@@ -29,7 +30,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -40,13 +43,15 @@ import android.util.Log;
*
* @author jsharkey
*/
-public class TerminalManager extends Service {
+public class TerminalManager extends Service implements BridgeDisconnectedListener {
public final static String TAG = TerminalManager.class.toString();
public List<TerminalBridge> bridges = new LinkedList<TerminalBridge>();
public TerminalBridge defaultBridge = null;
+ public List<String> disconnected = new LinkedList<String>();
+
protected SharedPreferences prefs;
protected String pref_emulation, pref_scrollback;
@@ -64,7 +69,7 @@ public class TerminalManager extends Service {
// disconnect and dispose of any existing bridges
for(TerminalBridge bridge : bridges)
- bridge.dispose();
+ bridge.disconnect();
}
@@ -83,8 +88,17 @@ public class TerminalManager extends Service {
scrollback = Integer.parseInt(prefs.getString(this.pref_scrollback, "140"));
} catch(Exception e) {
}
+
+ // 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);
+ bridge.disconnectListener = this;
+ bridge.postlogin = postlogin;
+ bridge.startLogin();
this.bridges.add(bridge);
// also update database with new connected time
@@ -126,14 +140,29 @@ public class TerminalManager extends Service {
}
return null;
}
+
+ public Handler parentHandler = null;
/**
* Force disconnection of this {@link TerminalBridge} and remove it from our
* internal list of active connections.
*/
public void disconnect(TerminalBridge bridge) {
- bridge.dispose();
+ // we will be notified about this through call back up to disconnected()
+ bridge.disconnect();
+ }
+
+ /**
+ * Called by child bridge when somehow it's been disconnected.
+ */
+ public void onDisconnected(TerminalBridge bridge) {
+ // remove this bridge from our list
this.bridges.remove(bridge);
+ this.disconnected.add(bridge.nickname);
+
+ // pass notification back up to gui
+ if(this.parentHandler != null)
+ Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_DISCONNECT, bridge).sendToTarget();
}
diff --git a/src/org/connectbot/util/HostBinder.java b/src/org/connectbot/util/HostBinder.java
index 927a740..39439c7 100644
--- a/src/org/connectbot/util/HostBinder.java
+++ b/src/org/connectbot/util/HostBinder.java
@@ -51,14 +51,19 @@ public class HostBinder implements ViewBinder {
}
+ public final static int STATE_UNKNOWN = 1, STATE_CONNECTED = 2, STATE_DISCONNECTED = 3;
+
/**
* Check if we're connected to a terminal with the given nickname.
*/
- protected boolean isConnected(String nickname) {
+ protected int getConnectedState(String nickname) {
// always disconnected if we dont have backend service
- if(this.manager == null) return false;
- return (this.manager.findBridge(nickname) != null);
-
+ if(this.manager == null) return STATE_UNKNOWN;
+ boolean connected = (this.manager.findBridge(nickname) != null);
+ boolean disconnected = (this.manager.disconnected.contains(nickname));
+ if(connected) return STATE_CONNECTED;
+ if(disconnected) return STATE_DISCONNECTED;
+ return STATE_UNKNOWN;
}
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
@@ -72,10 +77,16 @@ public class HostBinder implements ViewBinder {
COL_NICKNAME = cursor.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME);
String nickname = cursor.getString(COL_NICKNAME);
- if(this.isConnected(nickname)) {
- icon.setImageState(new int[] { android.R.attr.state_checked }, true);
- } else {
+ switch(this.getConnectedState(nickname)) {
+ case STATE_UNKNOWN:
icon.setImageState(new int[] { }, true);
+ break;
+ case STATE_CONNECTED:
+ icon.setImageState(new int[] { android.R.attr.state_checked }, true);
+ break;
+ case STATE_DISCONNECTED:
+ icon.setImageState(new int[] { android.R.attr.state_expanded }, true);
+ break;
}
return true;
diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java
index d5d1c22..0e4a30c 100644
--- a/src/org/connectbot/util/HostDatabase.java
+++ b/src/org/connectbot/util/HostDatabase.java
@@ -33,7 +33,7 @@ import android.database.sqlite.SQLiteOpenHelper;
public class HostDatabase extends SQLiteOpenHelper {
public final static String DB_NAME = "hosts";
- public final static int DB_VERSION = 8;
+ public final static int DB_VERSION = 9;
public final static String TABLE_HOSTS = "hosts";
public final static String FIELD_HOST_NICKNAME = "nickname";
@@ -44,6 +44,7 @@ public class HostDatabase extends SQLiteOpenHelper {
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 FIELD_HOST_POSTLOGIN = "postlogin";
public final static String COLOR_RED = "red";
public final static String COLOR_GREEN = "green";
@@ -65,7 +66,8 @@ public class HostDatabase extends SQLiteOpenHelper {
+ FIELD_HOST_HOSTKEY + " TEXT, "
+ FIELD_HOST_LASTCONNECT + " INTEGER, "
+ FIELD_HOST_COLOR + " TEXT, "
- + FIELD_HOST_USEKEYS + " 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);
@@ -147,5 +149,24 @@ public class HostDatabase extends SQLiteOpenHelper {
}
+ /**
+ * Find the post-login command string for the given nickname.
+ */
+ public String getPostLogin(String nickname) {
+
+ String result = null;
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_POSTLOGIN },
+ FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null);
+ if(c == null || !c.moveToFirst()) {
+ result = null;
+ } else {
+ result = c.getString(c.getColumnIndexOrThrow(FIELD_HOST_POSTLOGIN));
+ }
+
+ return result;
+
+ }
+
}