aboutsummaryrefslogtreecommitdiffstats
path: root/src/org
diff options
context:
space:
mode:
authorJeffrey Sharkey <jsharkey@jsharkey.org>2008-11-02 23:12:26 +0000
committerJeffrey Sharkey <jsharkey@jsharkey.org>2008-11-02 23:12:26 +0000
commit7a4f3578afc79f409ed8da118d9809557cf5ec46 (patch)
tree4f5b5b9e936842e24c07ce3c77c5f71ca9926248 /src/org
parentccc3a32792175a561fc2c75ccc4fd9f441295830 (diff)
downloadconnectbot-7a4f3578afc79f409ed8da118d9809557cf5ec46.tar.gz
connectbot-7a4f3578afc79f409ed8da118d9809557cf5ec46.tar.bz2
connectbot-7a4f3578afc79f409ed8da118d9809557cf5ec46.zip
* added "in-memory" function to backend service so that unlocked keys can be stored there. this is also disable-able from settings
* "use any key" for a host will only look through unlocked in-memory keys * implemented "load on start" functionality in backend service * implemented "import key" which lets you select any openssh-formatted key (including passworded-ones) from simple /sdcard browser * cleaned up context menu in pubkeylist, now includes toggle checkbox for "load at start" but only available when password-less and non-imported * clicking a key in pubkeylist will toggle its backend status (decrypt and put in memory, or remove from memory) * created preference for screen orientation forcing versus auto, but still need to test * created preference for camera button behavior, but still need to test
Diffstat (limited to 'src/org')
-rw-r--r--src/org/connectbot/ConsoleActivity.java12
-rw-r--r--src/org/connectbot/GeneratePubkeyActivity.java9
-rw-r--r--src/org/connectbot/HostEditorActivity.java70
-rw-r--r--src/org/connectbot/HostListActivity.java4
-rw-r--r--src/org/connectbot/PubkeyListActivity.java345
-rw-r--r--src/org/connectbot/service/TerminalBridge.java159
-rw-r--r--src/org/connectbot/service/TerminalManager.java94
-rw-r--r--src/org/connectbot/util/HostDatabase.java2
-rw-r--r--src/org/connectbot/util/KeyDatabase.java62
-rw-r--r--src/org/connectbot/util/PubkeyDatabase.java67
10 files changed, 622 insertions, 202 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index 76793a6..27afffa 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -30,6 +30,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -314,6 +315,15 @@ public class ConsoleActivity extends Activity {
this.clipboard = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE);
this.prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ // request a forced orientation if requested by user
+ String rotate = this.prefs.getString(getString(R.string.pref_rotation), getString(R.string.list_rotation_land));
+ if(getString(R.string.list_rotation_land).equals(rotate)) {
+ this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ } else if (getString(R.string.list_rotation_port).equals(rotate)) {
+ this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+
PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE);
wakelock = manager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
@@ -594,7 +604,7 @@ public class ConsoleActivity extends Activity {
public boolean onMenuItemClick(MenuItem item) {
// close the currently visible session
TerminalView terminal = (TerminalView)view;
- bound.disconnect(terminal.bridge);
+ terminal.bridge.dispatchDisconnect();
// movement should now be happening over in onDisconnect() handler
//flip.removeView(flip.getCurrentView());
//shiftLeft();
diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java
index 8115601..5e0e31d 100644
--- a/src/org/connectbot/GeneratePubkeyActivity.java
+++ b/src/org/connectbot/GeneratePubkeyActivity.java
@@ -55,9 +55,6 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
public final static String TAG = GeneratePubkeyActivity.class.toString();
- final static String KEY_TYPE_RSA = "RSA",
- KEY_TYPE_DSA = "DSA";
-
final static int DEFAULT_BITS = 1024;
protected LayoutInflater inflater = null;
@@ -73,7 +70,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
private EditText password1, password2;
- private String keyType = KEY_TYPE_RSA;
+ private String keyType = PubkeyDatabase.KEY_TYPE_RSA;
private int minBits = 768;
private int bits = DEFAULT_BITS;
@@ -117,7 +114,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setText(String.valueOf(DEFAULT_BITS));
bitsText.setEnabled(true);
- keyType = KEY_TYPE_RSA;
+ keyType = PubkeyDatabase.KEY_TYPE_RSA;
} else if (checkedId == R.id.dsa) {
// DSA keys can only be 1024 bits
@@ -127,7 +124,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setText(String.valueOf(DEFAULT_BITS));
bitsText.setEnabled(false);
- keyType = KEY_TYPE_DSA;
+ keyType = PubkeyDatabase.KEY_TYPE_DSA;
}
}
});
diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java
index 0ae921e..daa81a0 100644
--- a/src/org/connectbot/HostEditorActivity.java
+++ b/src/org/connectbot/HostEditorActivity.java
@@ -49,7 +49,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
protected final int id;
protected Map<String, String> values = new HashMap<String, String>();
- protected Map<String, String> pubkeys = new HashMap<String, String>();
+// protected Map<String, String> pubkeys = new HashMap<String, String>();
public CursorPreferenceHack(String table, int id) {
this.table = table;
@@ -78,21 +78,21 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
cursor.close();
db.close();
- db = pubkeydb.getReadableDatabase();
- cursor = db.query(PubkeyDatabase.TABLE_PUBKEYS,
- new String[] { "_id", PubkeyDatabase.FIELD_PUBKEY_NICKNAME },
- null, null, null, null, null);
-
- if (cursor.moveToFirst()) {
- do {
- String pubkeyid = String.valueOf(cursor.getLong(0));
- String value = cursor.getString(1);
- pubkeys.put(pubkeyid, value);
- } while (cursor.moveToNext());
- }
-
- cursor.close();
- db.close();
+// db = pubkeydb.getReadableDatabase();
+// cursor = db.query(PubkeyDatabase.TABLE_PUBKEYS,
+// new String[] { "_id", PubkeyDatabase.FIELD_PUBKEY_NICKNAME },
+// null, null, null, null, null);
+//
+// if (cursor.moveToFirst()) {
+// do {
+// String pubkeyid = String.valueOf(cursor.getLong(0));
+// String value = cursor.getString(1);
+// pubkeys.put(pubkeyid, value);
+// } while (cursor.moveToNext());
+// }
+//
+// cursor.close();
+// db.close();
}
public boolean contains(String key) {
@@ -110,7 +110,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
}
public boolean commit() {
- Log.d(this.getClass().toString(), "commit() changes back to database");
+ //Log.d(this.getClass().toString(), "commit() changes back to database");
SQLiteDatabase db = hostdb.getWritableDatabase();
db.update(table, update, "_id = ?", new String[] { Integer.toString(id) });
db.close();
@@ -143,13 +143,13 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
}
public android.content.SharedPreferences.Editor putString(String key, String value) {
- Log.d(this.getClass().toString(), String.format("Editor.putString(key=%s, value=%s)", key, value));
+ //Log.d(this.getClass().toString(), String.format("Editor.putString(key=%s, value=%s)", key, value));
update.put(key, value);
return this;
}
public android.content.SharedPreferences.Editor remove(String key) {
- Log.d(this.getClass().toString(), String.format("Editor.remove(key=%s)", key));
+ //Log.d(this.getClass().toString(), String.format("Editor.remove(key=%s)", key));
update.remove(key);
return this;
}
@@ -158,7 +158,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
public Editor edit() {
- Log.d(this.getClass().toString(), "edit()");
+ //Log.d(this.getClass().toString(), "edit()");
return new Editor();
}
@@ -183,7 +183,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
}
public String getString(String key, String defValue) {
- Log.d(this.getClass().toString(), String.format("getString(key=%s, defValue=%s)", key, defValue));
+ //Log.d(this.getClass().toString(), String.format("getString(key=%s, defValue=%s)", key, defValue));
if(!values.containsKey(key)) return defValue;
return values.get(key);
@@ -204,7 +204,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
- Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
+ //Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
return this.pref;
}
@@ -230,15 +230,17 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
this.addPreferencesFromResource(R.xml.host_prefs);
- // Grab all the pubkeys from the database cache we have.
+ // add all existing pubkeys to our listpreference for user to choose from
+ // TODO: may be an issue here when this activity is recycled after adding a new pubkey
+ // TODO: should consider moving into onStart, but we dont have a good way of resetting the listpref after filling once
ListPreference pubkeyPref = (ListPreference)this.findPreference(HostDatabase.FIELD_HOST_PUBKEYID);
-
+
List<CharSequence> pubkeyNicks = new LinkedList<CharSequence>(Arrays.asList(pubkeyPref.getEntries()));
- pubkeyNicks.addAll(this.pref.pubkeys.values());
+ pubkeyNicks.addAll(pubkeydb.allValues(PubkeyDatabase.FIELD_PUBKEY_NICKNAME));
pubkeyPref.setEntries((CharSequence[]) pubkeyNicks.toArray(new CharSequence[pubkeyNicks.size()]));
List<CharSequence> pubkeyIds = new LinkedList<CharSequence>(Arrays.asList(pubkeyPref.getEntryValues()));
- pubkeyIds.addAll(this.pref.pubkeys.keySet());
+ pubkeyIds.addAll(pubkeydb.allValues("_id"));
pubkeyPref.setEntryValues((CharSequence[]) pubkeyIds.toArray(new CharSequence[pubkeyIds.size()]));
this.updateSummaries();
@@ -249,6 +251,9 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
if(this.hostdb == null)
this.hostdb = new HostDatabase(this);
+ if(this.pubkeydb == null)
+ this.pubkeydb = new PubkeyDatabase(this);
+
}
public void onStop() {
@@ -257,6 +262,11 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
this.hostdb.close();
this.hostdb = null;
}
+
+ if(this.pubkeydb != null) {
+ this.pubkeydb.close();
+ this.pubkeydb = null;
+ }
}
public void updateSummaries() {
@@ -272,14 +282,18 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
try {
int pubkeyId = Integer.parseInt(value);
if (pubkeyId >= 0)
- pref.setSummary(this.pref.pubkeys.get(this.pref.getString(key, "")));
+ pref.setSummary(pubkeydb.getNickname(pubkeyId));
+ else if(pubkeyId == HostDatabase.PUBKEYID_ANY)
+ pref.setSummary(R.string.list_pubkeyids_any);
+ else if(pubkeyId == HostDatabase.PUBKEYID_NEVER)
+ pref.setSummary(R.string.list_pubkeyids_none);
continue;
} catch (NumberFormatException nfe) {
// Fall through.
}
}
- pref.setSummary(this.pref.getString(key, ""));
+ pref.setSummary(value);
}
}
diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java
index cda2131..429d4e0 100644
--- a/src/org/connectbot/HostListActivity.java
+++ b/src/org/connectbot/HostListActivity.java
@@ -388,7 +388,7 @@ public class HostListActivity extends ListActivity {
connect.setEnabled((bridge != null));
connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
- bound.disconnect(bridge);
+ bridge.dispatchDisconnect();
updateHandler.sendEmptyMessage(-1);
return true;
}
@@ -414,7 +414,7 @@ public class HostListActivity extends ListActivity {
public void onClick(DialogInterface dialog, int which) {
// make sure we disconnect
if(bridge != null)
- bound.disconnect(bridge);
+ bridge.dispatchDisconnect();
hostdb.deleteHost(id);
updateHandler.sendEmptyMessage(-1);
diff --git a/src/org/connectbot/PubkeyListActivity.java b/src/org/connectbot/PubkeyListActivity.java
index 095a1d8..b7fd9ae 100644
--- a/src/org/connectbot/PubkeyListActivity.java
+++ b/src/org/connectbot/PubkeyListActivity.java
@@ -18,20 +18,40 @@
package org.connectbot;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
import java.util.EventListener;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import org.connectbot.service.TerminalManager;
import org.connectbot.util.PubkeyDatabase;
import org.connectbot.util.PubkeyUtils;
+import com.trilead.ssh2.crypto.PEMDecoder;
+import com.trilead.ssh2.crypto.PEMStructure;
+
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.DialogInterface.OnClickListener;
import android.database.Cursor;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.text.ClipboardManager;
import android.util.Log;
@@ -48,6 +68,8 @@ import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TableRow;
import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemClickListener;
import android.widget.SimpleCursorAdapter.ViewBinder;
public class PubkeyListActivity extends ListActivity implements EventListener {
@@ -61,24 +83,43 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
protected ClipboardManager clipboard;
protected LayoutInflater inflater = null;
+
+ protected TerminalManager bound = null;
+
+ private ServiceConnection connection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ bound = ((TerminalManager.TerminalBinder) service).getService();
+
+ // update our listview binder to find the service
+ PubkeyListActivity.this.updateCursor();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ bound = null;
+ PubkeyListActivity.this.updateCursor();
+ }
+ };
@Override
public void onStart() {
super.onStart();
+ this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
+
if(this.pubkeydb == null)
this.pubkeydb = new PubkeyDatabase(this);
-
- ListView list = this.getListView();
- this.registerForContextMenu(list);
}
@Override
public void onStop() {
super.onStop();
- if(this.pubkeydb != null)
+ this.unbindService(connection);
+
+ if(this.pubkeydb != null) {
this.pubkeydb.close();
+ this.pubkeydb = null;
+ }
}
@Override
@@ -91,6 +132,8 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
this.updateCursor();
+ this.registerForContextMenu(this.getListView());
+
this.COL_ID = pubkeys.getColumnIndexOrThrow("_id");
this.COL_NICKNAME = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_NICKNAME);
this.COL_TYPE = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_TYPE);
@@ -99,61 +142,234 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
this.COL_ENCRYPTED = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED);
this.COL_STARTUP = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_STARTUP);
+ this.getListView().setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
+ Cursor cursor = (Cursor) getListView().getItemAtPosition(position);
+ String nickname = cursor.getString(COL_NICKNAME);
+ boolean loaded = bound.isKeyLoaded(nickname);
+
+ // handle toggling key in-memory on/off
+ if(loaded) {
+ bound.removeKey(nickname);
+ updateHandler.sendEmptyMessage(-1);
+ } else {
+ handleAddKey(cursor);
+ }
+
+ }
+ });
+
this.clipboard = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE);
this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
+ /**
+ * Read given file into memory as <code>byte[]</code>.
+ */
+ public static byte[] readRaw(File file) throws Exception {
+ InputStream is = new FileInputStream(file);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ int bytesRead;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = is.read(buffer)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ }
+
+ os.flush();
+ os.close();
+ is.close();
+
+ return os.toByteArray();
+
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem generatekey = menu.add(R.string.pubkey_generate);
- generatekey.setIcon(android.R.drawable.ic_lock_lock);
+ generatekey.setIcon(android.R.drawable.ic_menu_manage);
generatekey.setIntent(new Intent(PubkeyListActivity.this, GeneratePubkeyActivity.class));
- // TODO: allow importing of keys
- //MenuItem importkey = menu.add("Import");
- //importkey.setIcon(android.R.drawable.ic_lock_lock);
- //importkey.setIntent(new Intent(PubkeyListActivity.this, ImportPubkeyActivity.class));
+ MenuItem importkey = menu.add("Import");
+ importkey.setIcon(android.R.drawable.ic_menu_upload);
+ importkey.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+
+ // TODO: replace this with well-known intent over to file browser
+ // TODO: if browser not installed (?) then fallback to this simple method?
+
+ // build list of all files in sdcard root
+ final File sdcard = Environment.getExternalStorageDirectory();
+ Log.d(TAG, sdcard.toString());
+ List<String> names = new LinkedList<String>();
+ for(File file : sdcard.listFiles()) {
+ if(file.isDirectory()) continue;
+ names.add(file.getName());
+ }
+ final String[] namesList = names.toArray(new String[] {});
+ Log.d(TAG, names.toString());
+
+ // prompt user to select any file from the sdcard root
+ new AlertDialog.Builder(PubkeyListActivity.this)
+ .setTitle("Pick from /sdcard")
+ .setItems(namesList, new OnClickListener() {
+ public void onClick(DialogInterface arg0, int arg1) {
+ // find the exact file selected
+ String name = namesList[arg1];
+ File actual = new File(sdcard, name);
+
+ // parse the actual key once to check if its encrypted
+ // then save original file contents into our database
+ try {
+ byte[] raw = readRaw(actual);
+ PEMStructure struct = PEMDecoder.parsePEM(new String(raw).toCharArray());
+ boolean encrypted = PEMDecoder.isPEMEncrypted(struct);
+
+ // write new value into database
+ pubkeydb.createPubkey(null, name, PubkeyDatabase.KEY_TYPE_IMPORTED, raw, new byte[] {}, encrypted, false);
+ updateHandler.sendEmptyMessage(-1);
+
+ } catch(Exception e) {
+ Log.e(TAG, "Problem parsing imported private key", e);
+ Toast.makeText(PubkeyListActivity.this, "Problem parsing imported private key", Toast.LENGTH_LONG).show();
+ }
+ }
+ })
+ .setNegativeButton("Cancel", null).create().show();
+
+ return true;
+ }
+ });
return true;
}
+ protected void handleAddKey(final Cursor c) {
+ int encrypted = c.getInt(COL_ENCRYPTED);
+
+ if(encrypted != 0) {
+ final View view = inflater.inflate(R.layout.dia_password, null);
+ final EditText passwordField = (EditText)view.findViewById(android.R.id.text1);
+
+ new AlertDialog.Builder(PubkeyListActivity.this)
+ .setView(view)
+ .setPositiveButton("Unlock key", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ handleAddKey(c, passwordField.getText().toString());
+ }
+ })
+ .setNegativeButton("Cancel", null).create().show();
+ } else {
+ handleAddKey(c, null);
+
+ }
+
+
+ }
+
+ protected void handleAddKey(Cursor c, String password) {
+ String keyNickname = c.getString(COL_NICKNAME);
+ Object trileadKey = null;
+ String type = c.getString(COL_TYPE);
+ if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(type)) {
+ // load specific key using pem format
+ byte[] raw = c.getBlob(COL_PRIVATE);
+ try {
+ trileadKey = PEMDecoder.decode(new String(raw).toCharArray(), password);
+ } catch(Exception e) {
+ String message = String.format("Bad password for key '%s'. Authentication failed.", keyNickname);
+ Log.e(TAG, message, e);
+ Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG);
+ }
+
+ } else {
+ // load using internal generated format
+ PrivateKey privKey = null;
+ PublicKey pubKey = null;
+ try {
+ privKey = PubkeyUtils.decodePrivate(c.getBlob(COL_PRIVATE), c.getString(COL_TYPE), password);
+ pubKey = PubkeyUtils.decodePublic(c.getBlob(COL_PUBLIC), c.getString(COL_TYPE));
+ } catch (Exception e) {
+ String message = String.format("Bad password for key '%s'. Authentication failed.", keyNickname);
+ Log.e(TAG, message, e);
+ Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG);
+ }
+
+ // convert key to trilead format
+ trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
+ Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
+ }
+
+ if(trileadKey == null) return;
+
+ Log.d(TAG, String.format("Unlocked key '%s'", keyNickname));
+
+ // save this key in-memory if option enabled
+ if(bound.isSavingKeys()) {
+ bound.addKey(keyNickname, trileadKey);
+ }
+
+ updateHandler.sendEmptyMessage(-1);
+
+
+ }
+
+ protected MenuItem onstartToggle = null;
+
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
// Create menu to handle deleting and editing pubkey
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Cursor cursor = (Cursor) this.getListView().getItemAtPosition(info.position);
+ final Cursor cursor = (Cursor) this.getListView().getItemAtPosition(info.position);
final String nickname = cursor.getString(COL_NICKNAME);
menu.setHeaderTitle(nickname);
+ // TODO: option load/unload key from in-memory list
+ // prompt for password as needed for passworded keys
+
final int id = cursor.getInt(COL_ID);
final byte[] pubkeyEncoded = cursor.getBlob(COL_PUBLIC);
final String keyType = cursor.getString(COL_TYPE);
final int encrypted = cursor.getInt(COL_ENCRYPTED);
+
+ // cant change password or clipboard imported keys
+ boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(cursor.getString(COL_TYPE));
+ final boolean loaded = bound.isKeyLoaded(nickname);
+ final boolean onstart = (cursor.getInt(COL_STARTUP) == 1);
- MenuItem delete = menu.add(R.string.pubkey_delete);
- delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ MenuItem load = menu.add(loaded ? "Unload from memory" : "Load into memory");
+ load.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
- // prompt user to make sure they really want this
- new AlertDialog.Builder(PubkeyListActivity.this)
- .setMessage(getString(R.string.delete_message, nickname))
- .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- pubkeydb.deletePubkey(id);
- updateHandler.sendEmptyMessage(-1);
- }
- })
- .setNegativeButton(R.string.delete_neg, null).create().show();
-
+ if(loaded) {
+ bound.removeKey(nickname);
+ updateHandler.sendEmptyMessage(-1);
+ } else {
+ handleAddKey(cursor);
+ //bound.addKey(nickname, trileadKey);
+ }
return true;
}
});
+ onstartToggle = menu.add("Load key on start");
+ onstartToggle.setEnabled((encrypted == 0));
+ onstartToggle.setCheckable(true);
+ onstartToggle.setChecked(onstart);
+ onstartToggle.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // toggle onstart status
+ pubkeydb.setOnStart(id, !onstart);
+ updateHandler.sendEmptyMessage(-1);
+ return true;
+ }
+ });
+
MenuItem copyToClipboard = menu.add(R.string.pubkey_copy_clipboard);
+ copyToClipboard.setEnabled(!imported);
copyToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
try {
@@ -169,6 +385,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
});
MenuItem changePassword = menu.add(R.string.pubkey_change_password);
+ changePassword.setEnabled(!imported);
changePassword.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
final View changePasswordView = inflater.inflate(R.layout.dia_changepassword, null, false);
@@ -199,8 +416,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
else
updateHandler.sendEmptyMessage(-1);
} catch (Exception e) {
- Log.e(TAG, "Could not change private key password");
- e.printStackTrace();
+ Log.e(TAG, "Could not change private key password", e);
new AlertDialog.Builder(PubkeyListActivity.this)
.setMessage(R.string.alert_key_corrupted_msg)
.setPositiveButton(android.R.string.ok, null)
@@ -213,8 +429,28 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
return true;
}
});
+
+ MenuItem delete = menu.add(R.string.pubkey_delete);
+ delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // prompt user to make sure they really want this
+ new AlertDialog.Builder(PubkeyListActivity.this)
+ .setMessage(getString(R.string.delete_message, nickname))
+ .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ pubkeydb.deletePubkey(id);
+ updateHandler.sendEmptyMessage(-1);
+ }
+ })
+ .setNegativeButton(R.string.delete_neg, null).create().show();
+
+ return true;
+ }
+ });
+
}
+
public Handler updateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -224,7 +460,8 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
protected void updateCursor() {
if (this.pubkeys != null)
- pubkeys.requery();
+ pubkeys.close();
+ //pubkeys.requery();
if (this.pubkeydb == null) return;
@@ -236,7 +473,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
adapter.setViewBinder(new PubkeyBinder());
this.setListAdapter(adapter);
- this.startManagingCursor(pubkeys);
+ //this.startManagingCursor(pubkeys);
}
class PubkeyBinder implements ViewBinder {
@@ -244,25 +481,55 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
switch (view.getId()) {
case android.R.id.text2:
int encrypted = cursor.getInt(cursor.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED));
+ boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(cursor.getString(COL_TYPE));
+ TextView caption = (TextView)view;
- PublicKey pub;
- try {
- pub = PubkeyUtils.decodePublic(cursor.getBlob(cursor.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC)),
- cursor.getString(columnIndex));
- ((TextView)view).setText(PubkeyUtils.describeKey(pub, encrypted));
- } catch (Exception e) {
- e.printStackTrace();
+ if(imported) {
+ // for imported keys, have trilead parse them to get stats
+ try {
+ byte[] raw = cursor.getBlob(cursor.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PRIVATE));
+ PEMStructure struct = PEMDecoder.parsePEM(new String(raw).toCharArray());
+ String type = (struct.pemType == PEMDecoder.PEM_RSA_PRIVATE_KEY) ? "RSA" : "DSA";
+ caption.setText(String.format("%s unknown-bit", type));
+ } catch (IOException e) {
+ Log.e(TAG, "Error decoding IMPORTED public key at " + cursor.toString(), e);
+ }
+
- ((TextView)view).setText(R.string.pubkey_unknown_format);
- Log.e(TAG, "Error decoding public key at " + cursor.toString());
+ } else {
+
+ try {
+ PublicKey pub = PubkeyUtils.decodePublic(cursor.getBlob(cursor.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC)),
+ cursor.getString(columnIndex));
+ caption.setText(PubkeyUtils.describeKey(pub, encrypted));
+ } catch (Exception e) {
+ Log.e(TAG, "Error decoding public key at " + cursor.toString(), e);
+ caption.setText(R.string.pubkey_unknown_format);
+ }
}
+
return true;
case android.R.id.icon1:
- if (cursor.getInt(columnIndex) != 0)
- ((ImageView)view).setImageState(new int[] { android.R.attr.state_checked }, true);
+
+ ImageView icon = (ImageView)view;
+ if(bound == null) {
+ icon.setVisibility(View.GONE);
+ return true;
+
+ } else {
+ icon.setVisibility(View.VISIBLE);
+
+ }
+
+ // read key in-memory status from backend terminalmanager
+ String nickname = cursor.getString(COL_NICKNAME);
+ boolean loaded = bound.isKeyLoaded(nickname);
+
+ if(loaded)
+ icon.setImageState(new int[] { android.R.attr.state_checked }, true);
else
- ((ImageView)view).setImageState(new int[] { }, true);
+ icon.setImageState(new int[] { }, true);
return true;
}
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index 6d7a51c..48ad9d9 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -26,6 +26,7 @@ import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
+import org.connectbot.R;
import org.connectbot.TerminalView;
import org.connectbot.util.HostDatabase;
import org.connectbot.util.PubkeyDatabase;
@@ -51,6 +52,7 @@ import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.KnownHosts;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.Session;
+import com.trilead.ssh2.crypto.PEMDecoder;
import de.mud.terminal.VDUBuffer;
import de.mud.terminal.VDUDisplay;
@@ -124,9 +126,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
private boolean altPressed = false;
private boolean shiftPressed = false;
- protected PubkeyDatabase pubkeydb = null;
- protected Cursor pubkeys = null;
- final int COL_NICKNAME, COL_TYPE, COL_PRIVATE, COL_PUBLIC, COL_ENCRYPTED;
+ //protected PubkeyDatabase pubkeydb = null;
+ //protected Cursor pubkeys = null;
+ //final int COL_NICKNAME, COL_TYPE, COL_PRIVATE, COL_PUBLIC, COL_ENCRYPTED;
private boolean pubkeysExhausted = false;
private boolean forcedSize = false;
@@ -240,14 +242,14 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
this.connection = new Connection(hostname, port);
this.connection.addConnectionMonitor(this);
- this.pubkeydb = new PubkeyDatabase(manager);
- this.pubkeys = this.pubkeydb.allPubkeys();
-
- this.COL_NICKNAME = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_NICKNAME);
- this.COL_TYPE = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_TYPE);
- this.COL_PRIVATE = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PRIVATE);
- this.COL_PUBLIC = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC);
- this.COL_ENCRYPTED = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED);
+// this.pubkeydb = new PubkeyDatabase(manager);
+// this.pubkeys = this.pubkeydb.allPubkeys();
+//
+// this.COL_NICKNAME = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_NICKNAME);
+// this.COL_TYPE = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_TYPE);
+// this.COL_PRIVATE = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PRIVATE);
+// this.COL_PUBLIC = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC);
+// this.COL_ENCRYPTED = pubkeys.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED);
}
public final static int AUTH_TRIES = 20;
@@ -285,35 +287,72 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
* @throws IOException
*/
public boolean tryPublicKey(Cursor c) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+ int COL_NICKNAME = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_NICKNAME),
+ COL_TYPE = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_TYPE),
+ COL_PRIVATE = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PRIVATE),
+ COL_PUBLIC = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC),
+ COL_ENCRYPTED = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED);
+
String keyNickname = c.getString(COL_NICKNAME);
int encrypted = c.getInt(COL_ENCRYPTED);
- String password = null;
- if (encrypted != 0)
- password = promptHelper.requestStringPrompt(String.format("Password for key '%s'", keyNickname));
-
- PrivateKey privKey;
- try {
- privKey = PubkeyUtils.decodePrivate(c.getBlob(COL_PRIVATE),
- c.getString(COL_TYPE), password);
- } catch (Exception e) {
- e.printStackTrace();
- outputLine("Bad password for key '" + keyNickname + "'. Authentication failed.");
- return false;
+ Object trileadKey = null;
+ if(manager.isKeyLoaded(keyNickname)) {
+ // load this key from memory if its already there
+ Log.d(TAG, String.format("Found unlocked key '%s' already in-memory", keyNickname));
+ trileadKey = manager.getKey(keyNickname);
+
+ } else {
+ // otherwise load key from database and prompt for password as needed
+ String password = null;
+ if (encrypted != 0)
+ password = promptHelper.requestStringPrompt(String.format("Password for key '%s'", keyNickname));
+
+ String type = c.getString(COL_TYPE);
+ if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(type)) {
+ // load specific key using pem format
+ byte[] raw = c.getBlob(COL_PRIVATE);
+ trileadKey = PEMDecoder.decode(new String(raw).toCharArray(), password);
+
+ } else {
+ // load using internal generated format
+ PrivateKey privKey;
+ try {
+ privKey = PubkeyUtils.decodePrivate(c.getBlob(COL_PRIVATE),
+ c.getString(COL_TYPE), password);
+ } catch (Exception e) {
+ String message = String.format("Bad password for key '%s'. Authentication failed.", keyNickname);
+ Log.e(TAG, message, e);
+ outputLine(message);
+ return false;
+ }
+
+ PublicKey pubKey = PubkeyUtils.decodePublic(c.getBlob(COL_PUBLIC),
+ c.getString(COL_TYPE));
+
+ // convert key to trilead format
+ trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
+ Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
+ }
+
+ Log.d(TAG, String.format("Unlocked key '%s'", keyNickname));
+
+ // save this key in-memory if option enabled
+ if(manager.isSavingKeys()) {
+ manager.addKey(keyNickname, trileadKey);
+ }
}
+
+ return this.tryPublicKey(this.username, nickname, trileadKey);
- PublicKey pubKey = PubkeyUtils.decodePublic(c.getBlob(COL_PUBLIC),
- c.getString(COL_TYPE));
-
- Log.d("TerminalBridge", "Trying key " + PubkeyUtils.formatKey(pubKey));
-
- if (connection.authenticateWithPublicKey(username,
- PubkeyUtils.convertToTrilead(privKey, pubKey)))
- return true;
- else
- outputLine("Authentication method 'publickey' with key " + keyNickname + " failed");
-
- return false;
+ }
+
+ protected boolean tryPublicKey(String username, String keyNickname, Object trileadKey) throws IOException {
+ outputLine(String.format("Attempting 'publickey' with key '%s' [%s]...", keyNickname, trileadKey.toString()));
+ boolean success = connection.authenticateWithPublicKey(username, trileadKey);
+ if(!success)
+ outputLine(String.format("Authentication method 'publickey' with key '%s' failed", keyNickname));
+ return success;
}
public void handleAuthentication() {
@@ -334,25 +373,33 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
if (!pubkeysExhausted &&
pubkeyId != HostDatabase.PUBKEYID_NEVER &&
connection.isAuthMethodAvailable(username, AUTH_PUBLICKEY)) {
- Cursor cursor;
+
+ // if explicit pubkey defined for this host, then prompt for password as needed
+ // otherwise just try all in-memory keys held in terminalmanager
+ outputLine("Attempting 'publickey' authentication");
if (pubkeyId == HostDatabase.PUBKEYID_ANY) {
- cursor = pubkeydb.allPubkeys();
-
- while (cursor.moveToNext()) {
- if (cursor.getInt(COL_ENCRYPTED) == 0) {
- if (tryPublicKey(cursor))
- finishConnection();
+ // try each of the in-memory keys
+ outputLine("Trying any loaded SSH keys");
+ for(String nickname : manager.loadedPubkeys.keySet()) {
+ Object trileadKey = manager.loadedPubkeys.get(nickname);
+ if(this.tryPublicKey(this.username, nickname, trileadKey)) {
+ finishConnection();
+ break;
}
}
+
} else {
- cursor = pubkeydb.getPubkey(pubkeyId);
+ outputLine("Host settings requested a specific SSH key");
+ // use a specific key for this host, as requested
+ Cursor cursor = manager.pubkeydb.getPubkey(pubkeyId);
if (cursor.moveToFirst())
if (tryPublicKey(cursor))
finishConnection();
+ cursor.close();
+
}
- cursor.close();
pubkeysExhausted = true;
} else if (connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) {
outputLine("Attempting 'password' authentication");
@@ -491,7 +538,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
/**
* Force disconnection of this terminal bridge.
*/
- public void disconnect() {
+ public void dispatchDisconnect() {
// 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() {
@@ -619,8 +666,21 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
// look for special chars
switch(keyCode) {
case KeyEvent.KEYCODE_CAMERA:
- this.stdin.write(0x01);
- this.stdin.write(' ');
+
+ // check to see which shortcut the camera button triggers
+ String camera = manager.prefs.getString(manager.res.getString(R.string.pref_camera), manager.res.getString(R.string.list_camera_ctrlaspace));
+ if(manager.res.getString(R.string.list_camera_ctrlaspace).equals(camera)) {
+ this.stdin.write(0x01);
+ this.stdin.write(' ');
+
+ } else if(manager.res.getString(R.string.list_camera_ctrla).equals(camera)) {
+ this.stdin.write(0x01);
+
+ } else if(manager.res.getString(R.string.list_camera_esc).equals(camera)) {
+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
+
+ }
+
//((vt320)buffer).keyTyped('a', 'a', vt320.KEY_CONTROL);
//((vt320)buffer).keyTyped(' ', ' ', 0);
break;
@@ -759,9 +819,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
this.bitmap.recycle();
this.bitmap = null;
this.canvas.setBitmap(null);
-
- if (this.pubkeydb != null)
- this.pubkeydb.close();
}
public void setVDUBuffer(VDUBuffer buffer) {
@@ -862,7 +919,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal
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();
+ this.dispatchDisconnect();
}
/**
diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java
index 8559f5c..054e24f 100644
--- a/src/org/connectbot/service/TerminalManager.java
+++ b/src/org/connectbot/service/TerminalManager.java
@@ -18,15 +18,24 @@
package org.connectbot.service;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.connectbot.R;
import org.connectbot.util.HostDatabase;
+import org.connectbot.util.PubkeyDatabase;
+import org.connectbot.util.PubkeyUtils;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
@@ -51,9 +60,15 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
public List<String> disconnected = new LinkedList<String>();
+ protected HashMap<String, Object> loadedPubkeys = new HashMap<String, Object>();
+
+ protected Resources res;
+
protected HostDatabase hostdb;
+ protected PubkeyDatabase pubkeydb;
+
protected SharedPreferences prefs;
- protected String pref_emulation, pref_scrollback, pref_keymode;
+ protected String pref_emulation, pref_scrollback, pref_keymode, pref_memkeys;
@Override
public void onCreate() {
@@ -62,9 +77,36 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
this.pref_emulation = this.getResources().getString(R.string.pref_emulation);
this.pref_scrollback = this.getResources().getString(R.string.pref_scrollback);
this.pref_keymode = this.getResources().getString(R.string.pref_keymode);
+ this.pref_memkeys = this.getResources().getString(R.string.pref_memkeys);
+
+ this.res = this.getResources();
this.hostdb = new HostDatabase(this);
+ this.pubkeydb = new PubkeyDatabase(this);
+
+ // load all marked pubkeys into memory
+ Cursor c = pubkeydb.getAllStartPubkeys();
+ int COL_NICKNAME = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_NICKNAME),
+ COL_TYPE = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_TYPE),
+ COL_PRIVATE = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PRIVATE),
+ COL_PUBLIC = c.getColumnIndexOrThrow(PubkeyDatabase.FIELD_PUBKEY_PUBLIC);
+
+ while(c.moveToNext()) {
+ String keyNickname = c.getString(COL_NICKNAME);
+ try {
+ PrivateKey privKey = PubkeyUtils.decodePrivate(c.getBlob(COL_PRIVATE), c.getString(COL_TYPE));
+ PublicKey pubKey = PubkeyUtils.decodePublic(c.getBlob(COL_PUBLIC), c.getString(COL_TYPE));
+ Object trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
+
+ this.loadedPubkeys.put(keyNickname, trileadKey);
+ Log.d(TAG, String.format("Added key '%s' to in-memory cache", keyNickname));
+ } catch (Exception e) {
+ Log.d(TAG, String.format("Problem adding key '%s' to in-memory cache", keyNickname), e);
+ }
+ }
+ c.close();
+
}
@Override
@@ -73,10 +115,17 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
// disconnect and dispose of any existing bridges
for(TerminalBridge bridge : bridges)
- bridge.disconnect();
+ bridge.dispatchDisconnect();
- if(this.hostdb != null)
+ if(this.hostdb != null) {
this.hostdb.close();
+ this.hostdb = null;
+ }
+
+ if(this.pubkeydb != null) {
+ this.pubkeydb.close();
+ this.pubkeydb = null;
+ }
}
@@ -112,12 +161,16 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
return scrollback;
}
+ public boolean isSavingKeys() {
+ return prefs.getBoolean(this.pref_memkeys, true);
+ }
+
public String getPostLogin(String nickname) {
return hostdb.getPostLogin(nickname);
}
public String getKeyMode() {
- return prefs.getString(this.pref_keymode, "Use right-side keys");
+ return prefs.getString(this.pref_keymode, getString(R.string.list_keymode_right)); // "Use right-side keys"
}
/**
@@ -155,14 +208,14 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
public Handler disconnectHandler = null;
- /**
- * Force disconnection of this {@link TerminalBridge} and remove it from our
- * internal list of active connections.
- */
- public void disconnect(TerminalBridge bridge) {
- // we will be notified about this through call back up to onDisconnected()
- bridge.disconnect();
- }
+// /**
+// * Force disconnection of this {@link TerminalBridge} and remove it from our
+// * internal list of active connections.
+// */
+// public void disconnect(TerminalBridge bridge) {
+// // we will be notified about this through call back up to onDisconnected()
+// bridge.disconnect();
+// }
/**
* Called by child bridge when somehow it's been disconnected.
@@ -177,6 +230,23 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
Message.obtain(this.disconnectHandler, -1, bridge).sendToTarget();
}
+
+ public boolean isKeyLoaded(String nickname) {
+ return this.loadedPubkeys.containsKey(nickname);
+ }
+
+ public void addKey(String nickname, Object trileadKey) {
+ this.loadedPubkeys.remove(nickname);
+ this.loadedPubkeys.put(nickname, trileadKey);
+ }
+
+ public void removeKey(String nickname) {
+ this.loadedPubkeys.remove(nickname);
+ }
+
+ public Object getKey(String nickname) {
+ return this.loadedPubkeys.get(nickname);
+ }
public class TerminalBinder extends Binder {
diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java
index 3556875..fe4c8d4 100644
--- a/src/org/connectbot/util/HostDatabase.java
+++ b/src/org/connectbot/util/HostDatabase.java
@@ -79,7 +79,7 @@ public class HostDatabase extends SQLiteOpenHelper {
+ FIELD_HOST_COLOR + " TEXT, "
+ FIELD_HOST_USEKEYS + " TEXT, "
+ FIELD_HOST_POSTLOGIN + " TEXT, "
- + FIELD_HOST_PUBKEYID + " INTEGER DEFAULT " + PUBKEYID_ANY);
+ + FIELD_HOST_PUBKEYID + " INTEGER DEFAULT " + PUBKEYID_ANY + ")");
// insert a few sample hosts, none of which probably connect
//this.createHost(db, "connectbot@bravo", "connectbot", "192.168.254.230", 22, COLOR_GRAY);
diff --git a/src/org/connectbot/util/KeyDatabase.java b/src/org/connectbot/util/KeyDatabase.java
deleted file mode 100644
index aaca3c1..0000000
--- a/src/org/connectbot/util/KeyDatabase.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- 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.util;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-
-/**
- * Contains information about personal private keys used for key-based
- * authentication. Find more information here:
- *
- * http://www.wlug.org.nz/PublicKeyAuthentication
- *
- * @author jsharkey
- */
-public class KeyDatabase extends SQLiteOpenHelper {
-
- public final static String DB_NAME = "keys";
- public final static int DB_VERSION = 1;
-
- public final static String TABLE_PRIVKEYS = "keys";
- public final static String FIELD_KEY_NAME = "name";
- public final static String FIELD_KEY_PRIVATE = "private";
-
- public KeyDatabase(Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_PRIVKEYS
- + " (_id INTEGER PRIMARY KEY, "
- + FIELD_KEY_NAME + " TEXT, "
- + FIELD_KEY_PRIVATE + " TEXT)");
-
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRIVKEYS);
- onCreate(db);
- }
-
-
-}
diff --git a/src/org/connectbot/util/PubkeyDatabase.java b/src/org/connectbot/util/PubkeyDatabase.java
index 1c3278b..d3fa3d2 100644
--- a/src/org/connectbot/util/PubkeyDatabase.java
+++ b/src/org/connectbot/util/PubkeyDatabase.java
@@ -21,17 +21,23 @@ package org.connectbot.util;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
+import java.util.LinkedList;
+import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import org.connectbot.service.PromptHelper;
+
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;
/**
* Public Key Encryption database. Contains private and public key pairs
@@ -54,6 +60,10 @@ public class PubkeyDatabase extends SQLiteOpenHelper {
public final static String FIELD_PUBKEY_ENCRYPTED = "encrypted";
public final static String FIELD_PUBKEY_STARTUP = "startup";
+ public final static String KEY_TYPE_RSA = "RSA",
+ KEY_TYPE_DSA = "DSA",
+ KEY_TYPE_IMPORTED = "IMPORTED";
+
private Context context;
public PubkeyDatabase(Context context) {
@@ -133,6 +143,63 @@ public class PubkeyDatabase extends SQLiteOpenHelper {
null, null, null);
}
+ public Cursor getAllStartPubkeys() {
+ SQLiteDatabase db = this.getReadableDatabase();
+ return db.query(TABLE_PUBKEYS, new String[] { "_id",
+ FIELD_PUBKEY_NICKNAME, FIELD_PUBKEY_TYPE, FIELD_PUBKEY_PRIVATE,
+ FIELD_PUBKEY_PUBLIC, FIELD_PUBKEY_ENCRYPTED, FIELD_PUBKEY_STARTUP },
+ FIELD_PUBKEY_STARTUP + " = 1", null, null, null, null);
+ }
+
+
+ /**
+ * Pull all values for a given column as a list of Strings, probably for use
+ * in a ListPreference. Sorted by <code>_id</code> ascending.
+ */
+ public List<CharSequence> allValues(String column) {
+ List<CharSequence> list = new LinkedList<CharSequence>();
+
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor c = db.query(TABLE_PUBKEYS, new String[] { "_id", column },
+ null, null, null, null, "_id ASC");
+
+ int COL = c.getColumnIndexOrThrow(column);
+ while(c.moveToNext()) {
+ list.add(c.getString(COL));
+ }
+ c.close();
+
+ return list;
+ }
+
+ public String getNickname(long id) {
+ String nickname = null;
+
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor c = db.query(TABLE_PUBKEYS, new String[] { "_id",
+ FIELD_PUBKEY_NICKNAME }, "_id = ?",
+ new String[] { Long.toString(id) }, null, null, null);
+
+ if (c != null && c.moveToFirst())
+ nickname = c.getString(c.getColumnIndexOrThrow(FIELD_PUBKEY_NICKNAME));
+
+ c.close();
+ return nickname;
+
+ }
+
+ public void setOnStart(long id, boolean onStart) {
+
+ SQLiteDatabase db = this.getWritableDatabase();
+
+ ContentValues values = new ContentValues();
+ values.put(FIELD_PUBKEY_STARTUP, onStart ? 1 : 0);
+
+ db.update(TABLE_PUBKEYS, values, "_id = ?", new String[] { Long.toString(id) });
+
+ }
+
+
public boolean changePassword(long id, String oldPassword, String newPassword) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException {
SQLiteDatabase db = this.getWritableDatabase();