From 12ef92bf1319f2ce698bb979c49f781c2a884316 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 14 Nov 2008 12:07:29 +0000 Subject: * Convert host entries to beans * We no longer lose connections to hosts not in the database made from shortcuts --- src/org/connectbot/ConsoleActivity.java | 20 +- src/org/connectbot/HostListActivity.java | 267 +++++++++++++------- src/org/connectbot/PortForwardListActivity.java | 22 +- src/org/connectbot/bean/HostBean.java | 184 ++++++++++++++ src/org/connectbot/bean/PortForwardBean.java | 2 + src/org/connectbot/service/TerminalBridge.java | 66 ++--- src/org/connectbot/service/TerminalManager.java | 44 ++-- src/org/connectbot/util/HostBinder.java | 144 ----------- src/org/connectbot/util/HostDatabase.java | 321 +++++++++++------------- 9 files changed, 581 insertions(+), 489 deletions(-) create mode 100644 src/org/connectbot/bean/HostBean.java delete mode 100644 src/org/connectbot/util/HostBinder.java (limited to 'src') diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index d93a0ce..53cecfc 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -18,11 +18,11 @@ package org.connectbot; +import org.connectbot.bean.HostBean; import org.connectbot.bean.PortForwardBean; import org.connectbot.service.PromptHelper; import org.connectbot.service.TerminalBridge; import org.connectbot.service.TerminalManager; -import org.connectbot.util.HostDatabase; import android.app.Activity; import android.app.AlertDialog; @@ -92,7 +92,7 @@ public class ConsoleActivity extends Activity { // first check if we need to create a new session for requested boolean found = false; for(TerminalBridge bridge : bound.bridges) { - if(bridge.nickname.equals(requestedNickname)) + if(bridge.host.getNickname().equals(requestedNickname)) found = true; } @@ -118,7 +118,7 @@ public class ConsoleActivity extends Activity { // set the terminal overlay text TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay); - overlay.setText(bridge.nickname); + overlay.setText(bridge.host.getNickname()); // and add our terminal view control, using index to place behind overlay TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge); @@ -129,7 +129,7 @@ public class ConsoleActivity extends Activity { flip.addView(view); // check to see if this bridge was requested - if(bridge.nickname.equals(requestedNickname)) + if(bridge.host.getNickname().equals(requestedNickname)) requestedIndex = flip.getChildCount() - 1; } @@ -159,10 +159,10 @@ public class ConsoleActivity extends Activity { } }; - protected String getCurrentNickname() { + protected HostBean getCurrentHost() { View view = findCurrentView(R.id.console_flip); if(!(view instanceof TerminalView)) return null; - return ((TerminalView)view).bridge.nickname; + return ((TerminalView)view).bridge.host; } public Handler promptHandler = new Handler() { @@ -625,7 +625,7 @@ public class ConsoleActivity extends Activity { boolean sessionOpen = false; if(activeTerminal) { authenticated = ((TerminalView) view).bridge.isAuthenticated(); - sessionOpen = ((TerminalView)view).bridge.isSessionOpen(); + sessionOpen = ((TerminalView) view).bridge.isSessionOpen(); } disconnect = menu.add(R.string.console_menu_disconnect); @@ -682,7 +682,7 @@ public class ConsoleActivity extends Activity { portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class); - intent.putExtra(Intent.EXTRA_TITLE, bound.hostdb.findHostByNickname(getCurrentNickname())); + intent.putExtra(Intent.EXTRA_TITLE, ((TerminalView) view).bridge.host.getId()); ConsoleActivity.this.startActivityForResult(intent, REQUEST_EDIT); return true; } @@ -712,14 +712,12 @@ public class ConsoleActivity extends Activity { }); return true; - } protected void createPortForward(TerminalView target, String nickname, String type, String source, String dest) { String summary = getString(R.string.portforward_problem); try { - HostDatabase hostdb = new HostDatabase(this); - long hostId = hostdb.findHostByNickname(target.bridge.nickname); + long hostId = target.bridge.host.getId(); PortForwardBean pfb = new PortForwardBean(hostId, nickname, type, source, dest); diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index 15560d3..86769b4 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -18,11 +18,12 @@ package org.connectbot; +import java.util.List; import java.util.regex.Pattern; +import org.connectbot.bean.HostBean; 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; @@ -37,56 +38,64 @@ import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.Intent.ShortcutIconResource; import android.content.SharedPreferences.Editor; -import android.database.Cursor; +import android.content.res.ColorStateList; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.preference.PreferenceManager; +import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.MenuItem.OnMenuItemClickListener; import android.view.View.OnKeyListener; import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; import android.widget.ListView; -import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; public class HostListActivity extends ListActivity { - + protected TerminalManager bound = null; + + protected HostDatabase hostdb; + protected List hosts; + protected LayoutInflater inflater = null; + + public boolean sortedByColor = false; + + public MenuItem sortcolor; + + public MenuItem sortlast; + public Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { - HostListActivity.this.updateCursor(); + HostListActivity.this.updateList(); } }; - - 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 - HostListActivity.this.updateCursor(); + HostListActivity.this.updateList(); } - + public void onServiceDisconnected(ComponentName className) { bound = null; - HostListActivity.this.updateCursor(); + HostListActivity.this.updateList(); } }; - protected HostDatabase hostdb; - protected Cursor hosts; - - protected int COL_ID, COL_NICKNAME, COL_USERNAME, COL_HOSTNAME, COL_CONNECTED, COL_PORT; - @Override public void onStart() { super.onStart(); @@ -98,7 +107,7 @@ public class HostListActivity extends ListActivity { if(this.hostdb == null) this.hostdb = new HostDatabase(this); - this.updateCursor(); + this.updateList(); } @@ -107,11 +116,6 @@ public class HostListActivity extends ListActivity { super.onStop(); this.unbindService(connection); - if(this.hosts != null) { - this.hosts.close(); - this.hosts = null; - } - if(this.hostdb != null) { this.hostdb.close(); this.hostdb = null; @@ -145,7 +149,7 @@ public class HostListActivity extends ListActivity { break; case REQUEST_EDIT: - this.updateCursor(); + this.updateList(); break; } @@ -175,9 +179,6 @@ public class HostListActivity extends ListActivity { // start thread to check for new version new UpdateHelper(this); - - - this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction()); // connect with hosts database and populate list @@ -185,30 +186,21 @@ public class HostListActivity extends ListActivity { ListView list = this.getListView(); this.sortedByColor = prefs.getBoolean(PREF_SORTBYCOLOR, false); - this.updateCursor(); + this.updateList(); //this.list.setSelector(R.drawable.highlight_disabled_pressed); - this.COL_ID = hosts.getColumnIndexOrThrow("_id"); - this.COL_NICKNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME); - this.COL_USERNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_USERNAME); - this.COL_HOSTNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_HOSTNAME); - this.COL_PORT = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_PORT); - this.COL_CONNECTED = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_LASTCONNECT); - list.setOnItemClickListener(new OnItemClickListener() { public synchronized void onItemClick(AdapterView parent, View view, int position, long id) { // launch off to console details - Cursor c = (Cursor)parent.getAdapter().getItem(position); - String username = c.getString(COL_USERNAME); - String hostname = c.getString(COL_HOSTNAME); - int port = c.getInt(COL_PORT); - String nickname = c.getString(COL_NICKNAME); + HostBean host = (HostBean) parent.getAdapter().getItem(position); // create a specific uri that represents this host - Uri uri = Uri.parse(String.format("ssh://%s@%s:%s/#%s", username, hostname, port, nickname)); + Uri uri = Uri.parse(String.format("ssh://%s@%s:%s/#%s", + host.getUsername(), host.getHostname(), + host.getPort(), host.getNickname())); Intent contents = new Intent(Intent.ACTION_VIEW, uri); contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -219,7 +211,7 @@ public class HostListActivity extends ListActivity { Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, contents); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, nickname); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, host.getNickname()); intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon); setResult(RESULT_OK, intent); @@ -228,13 +220,8 @@ public class HostListActivity extends ListActivity { } else { // otherwise just launch activity to show this host HostListActivity.this.startActivity(contents); - - - } - - + } } - }); this.registerForContextMenu(list); @@ -273,7 +260,10 @@ public class HostListActivity extends ListActivity { nickname = String.format("%s@%s:%d", username, hostname, port); } - hostdb.createHost(null, nickname, username, hostname, port, HostDatabase.COLOR_GRAY, HostDatabase.PUBKEYID_ANY); + HostBean host = new HostBean(nickname, username, hostname, port); + host.setColor(HostDatabase.COLOR_GRAY); + host.setPubkeyId(HostDatabase.PUBKEYID_ANY); + hostdb.saveHost(host); Intent intent = new Intent(HostListActivity.this, ConsoleActivity.class); intent.setData(Uri.parse(String.format("ssh://%s@%s:%s/#%s", username, hostname, port, nickname))); @@ -289,42 +279,18 @@ public class HostListActivity extends ListActivity { }); + this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - 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) - this.hosts.close(); - if(this.hostdb == null) return; - this.hosts = this.hostdb.allHosts(sortedByColor); - - SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item_host, this.hosts, - new String[] { HostDatabase.FIELD_HOST_NICKNAME, HostDatabase.FIELD_HOST_LASTCONNECT, HostDatabase.FIELD_HOST_LASTCONNECT, HostDatabase.FIELD_HOST_COLOR }, - new int[] { android.R.id.text1, android.R.id.text2, android.R.id.icon, android.R.id.content }); - adapter.setViewBinder(new HostBinder(bound, this.getResources())); - - //this.adapter = new HostAdapter(this, this.hosts); - this.setListAdapter(adapter); - - } - - @Override + @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - if (sortcolor != null) - sortcolor.setVisible(!sortedByColor); + // don't offer menus when creating shortcut + if (makingShortcut) return true; - if (sortlast != null) - sortlast.setVisible(sortedByColor); + sortcolor.setVisible(!sortedByColor); + sortlast.setVisible(sortedByColor); return true; } @@ -333,7 +299,7 @@ public class HostListActivity extends ListActivity { public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - // dont offer menus when creating shortcut + // don't offer menus when creating shortcut if(makingShortcut) return true; // add host, ssh keys, about @@ -342,7 +308,7 @@ public class HostListActivity extends ListActivity { sortcolor.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { sortedByColor = true; - updateCursor(); + updateList(); return true; } }); @@ -352,7 +318,7 @@ public class HostListActivity extends ListActivity { sortlast.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { sortedByColor = false; - updateCursor(); + updateList(); return true; } }); @@ -381,15 +347,13 @@ public class HostListActivity extends ListActivity { // create menu to handle deleting and sharing lists AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Cursor cursor = (Cursor) this.getListView().getItemAtPosition(info.position); + final HostBean host = (HostBean) this.getListView().getItemAtPosition(info.position); - final String nickname = cursor.getString(COL_NICKNAME); - menu.setHeaderTitle(nickname); - final long id = cursor.getLong(COL_ID); + menu.setHeaderTitle(host.getNickname()); // edit, disconnect, delete MenuItem connect = menu.add(R.string.list_host_disconnect); - final TerminalBridge bridge = bound.findBridge(nickname); + final TerminalBridge bridge = bound.findBridge(host); connect.setEnabled((bridge != null)); connect.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -403,7 +367,7 @@ public class HostListActivity extends ListActivity { edit.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class); - intent.putExtra(Intent.EXTRA_TITLE, id); + intent.putExtra(Intent.EXTRA_TITLE, host.getId()); HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT); return true; } @@ -413,7 +377,7 @@ public class HostListActivity extends ListActivity { portForwards.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(HostListActivity.this, PortForwardListActivity.class); - intent.putExtra(Intent.EXTRA_TITLE, id); + intent.putExtra(Intent.EXTRA_TITLE, host.getId()); HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT); return true; } @@ -424,14 +388,14 @@ public class HostListActivity extends ListActivity { public boolean onMenuItemClick(MenuItem item) { // prompt user to make sure they really want this new AlertDialog.Builder(HostListActivity.this) - .setMessage(getString(R.string.delete_message, nickname)) + .setMessage(getString(R.string.delete_message, host.getNickname())) .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // make sure we disconnect if(bridge != null) bridge.dispatchDisconnect(); - hostdb.deleteHost(id); + hostdb.deleteHost(host); updateHandler.sendEmptyMessage(-1); } }) @@ -440,7 +404,130 @@ public class HostListActivity extends ListActivity { return true; } }); + } + + protected void updateList() { + Editor edit = prefs.edit(); + edit.putBoolean(PREF_SORTBYCOLOR, sortedByColor); + edit.commit(); + + if (hostdb == null) + hostdb = new HostDatabase(this); + + hosts = hostdb.getHosts(sortedByColor); + + // Don't lose hosts that are connected via shortcuts but not in the database. + if (bound != null) { + for (TerminalBridge bridge : bound.bridges) { + if (!hosts.contains(bridge.host)) + hosts.add(0, bridge.host); + } + } + HostAdapter adapter = new HostAdapter(this, hosts, bound); + + this.setListAdapter(adapter); } + + class HostAdapter extends ArrayAdapter { + private List hosts; + private final TerminalManager manager; + private final ColorStateList red, green, blue; + + public final static int STATE_UNKNOWN = 1, STATE_CONNECTED = 2, STATE_DISCONNECTED = 3; + + public HostAdapter(Context context, List hosts, TerminalManager manager) { + super(context, R.layout.item_host, hosts); + + this.hosts = hosts; + this.manager = manager; + + red = context.getResources().getColorStateList(R.color.red); + green = context.getResources().getColorStateList(R.color.green); + blue = context.getResources().getColorStateList(R.color.blue); + } + + /** + * Check if we're connected to a terminal with the given host. + */ + protected int getConnectedState(HostBean host) { + // always disconnected if we dont have backend service + if (this.manager == null) + return STATE_UNKNOWN; + + if (manager.findBridge(host) != null) + return STATE_CONNECTED; + + if (manager.disconnected.contains(host)) + return STATE_DISCONNECTED; + + return STATE_UNKNOWN; + } + + public View getView(int position, View view, ViewGroup parent) { + if (view == null) + view = inflater.inflate(R.layout.item_host, null, false); + + TextView nickname = (TextView)view.findViewById(android.R.id.text1); + TextView caption = (TextView)view.findViewById(android.R.id.text2); + ImageView icon = (ImageView)view.findViewById(android.R.id.icon); + Log.d("HostAdapter", String.format("Checking position %d", position)); + HostBean host = hosts.get(position); + if (host == null) { + Log.e("HostAdapter", "What the fuck, host bean is null!"); + } + nickname.setText(host.getNickname()); + + switch(this.getConnectedState(host)) { + 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; + } + + ColorStateList chosen = null; + if (HostDatabase.COLOR_RED.equals(host.getColor())) + chosen = this.red; + else if (HostDatabase.COLOR_GREEN.equals(host.getColor())) + chosen = this.green; + else if (HostDatabase.COLOR_BLUE.equals(host.getColor())) + chosen = this.blue; + + if(chosen != null) { + // set color normally if not selected + nickname.setTextColor(chosen); + caption.setTextColor(chosen); + } else { + // selected, so revert back to default black text + nickname.setTextAppearance(view.getContext(), android.R.attr.textAppearanceLarge); + caption.setTextAppearance(view.getContext(), android.R.attr.textAppearanceSmall); + } + + long now = System.currentTimeMillis() / 1000; + + String nice = "never"; + if(host.getLastConnect() > 0) { + int minutes = (int)((now - host.getLastConnect()) / 60); + nice = view.getContext().getString(R.string.bind_minutes, minutes); + if (minutes >= 60) { + int hours = (minutes / 60); + nice = view.getContext().getString(R.string.bind_hours, hours); + if (hours >= 24) { + int days = (hours / 24); + nice = view.getContext().getString(R.string.bind_days, days); + } + } + } + + caption.setText(nice); + + return view; + } + } } diff --git a/src/org/connectbot/PortForwardListActivity.java b/src/org/connectbot/PortForwardListActivity.java index e2097fa..688669d 100644 --- a/src/org/connectbot/PortForwardListActivity.java +++ b/src/org/connectbot/PortForwardListActivity.java @@ -20,6 +20,7 @@ package org.connectbot; import java.util.List; +import org.connectbot.bean.HostBean; import org.connectbot.bean.PortForwardBean; import org.connectbot.service.TerminalBridge; import org.connectbot.service.TerminalManager; @@ -52,6 +53,7 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; +import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; @@ -73,7 +75,7 @@ public class PortForwardListActivity extends ListActivity { protected TerminalBridge hostBridge = null; protected LayoutInflater inflater = null; - private long hostId; + private HostBean host; @Override public void onStart() { @@ -101,25 +103,25 @@ public class PortForwardListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); - this.hostId = this.getIntent().getLongExtra(Intent.EXTRA_TITLE, -1); + long hostId = this.getIntent().getLongExtra(Intent.EXTRA_TITLE, -1); setContentView(R.layout.act_portforwardlist); // connect with hosts database and populate list this.hostdb = new HostDatabase(this); - final String hostNickname = hostdb.findNicknameById(hostId); + host = hostdb.findHostById(hostId); this.setTitle(String.format("%s: %s (%s)", getResources().getText(R.string.app_name), getResources().getText(R.string.title_port_forwards_list), - hostNickname)); + host.getNickname())); connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService(); for (TerminalBridge bridge: bound.bridges) { - if (bridge.nickname.equals(hostNickname)) { + if (bridge.host.equals(host)) { hostBridge = bridge; updateHandler.sendEmptyMessage(-1); Log.d(TAG, "Found host bridge; using that instead of database"); @@ -147,8 +149,10 @@ public class PortForwardListActivity extends ListActivity { if (hostBridge != null) { if (pfb.isEnabled()) hostBridge.disablePortForward(pfb); - else - hostBridge.enablePortForward(pfb); + else { + if (!hostBridge.enablePortForward(pfb)) + Toast.makeText(PortForwardListActivity.this, getString(R.string.portforward_problem), Toast.LENGTH_LONG).show(); + } updateHandler.sendEmptyMessage(-1); } @@ -201,7 +205,7 @@ public class PortForwardListActivity extends ListActivity { break; } - PortForwardBean pfb = new PortForwardBean(hostId, + PortForwardBean pfb = new PortForwardBean(host.getId(), nicknameEdit.getText().toString(), type, sourcePortEdit.getText().toString(), destEdit.getText().toString()); @@ -357,7 +361,7 @@ public class PortForwardListActivity extends ListActivity { this.portForwards = hostBridge.getPortForwards(); } else { if (this.hostdb == null) return; - this.portForwards = this.hostdb.getPortForwardsForHost(hostId); + this.portForwards = this.hostdb.getPortForwardsForHost(host); } PortForwardAdapter adapter = new PortForwardAdapter(this, this.portForwards); diff --git a/src/org/connectbot/bean/HostBean.java b/src/org/connectbot/bean/HostBean.java new file mode 100644 index 0000000..683ed15 --- /dev/null +++ b/src/org/connectbot/bean/HostBean.java @@ -0,0 +1,184 @@ +/* + 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 . +*/ +package org.connectbot.bean; + +import org.connectbot.util.HostDatabase; + +import android.content.ContentValues; + +/** + * @author kenny + * + */ +public class HostBean { + /* Database fields */ + private long id = -1; + private String nickname = null; + private String username = null; + private String hostname = null; + private int port = 22; + private String hostKeyAlgo = null; + private byte[] hostKey = null; + private long lastConnect = -1; + private String color; + private boolean useKeys = true; + private byte[] postLogin = null; + private long pubkeyId = -1; + private boolean wantSession = true; + private boolean compression = false; + + public HostBean() { + + } + + public HostBean(String nickname, String username, String hostname, int port) { + this.nickname = nickname; + this.username = username; + this.hostname = hostname; + this.port = port; + } + + public void setId(long id) { + this.id = id; + } + public long getId() { + return id; + } + public void setNickname(String nickname) { + this.nickname = nickname; + } + public String getNickname() { + return nickname; + } + public void setUsername(String username) { + this.username = username; + } + public String getUsername() { + return username; + } + public void setHostname(String hostname) { + this.hostname = hostname; + } + public String getHostname() { + return hostname; + } + public void setPort(int port) { + this.port = port; + } + public int getPort() { + return port; + } + public void setHostKeyAlgo(String hostKeyAlgo) { + this.hostKeyAlgo = hostKeyAlgo; + } + public String getHostKeyAlgo() { + return hostKeyAlgo; + } + public void setHostKey(byte[] hostKey) { + this.hostKey = hostKey; + } + public byte[] getHostKey() { + return hostKey; + } + public void setLastConnect(long lastConnect) { + this.lastConnect = lastConnect; + } + public long getLastConnect() { + return lastConnect; + } + public void setColor(String color) { + this.color = color; + } + public String getColor() { + return color; + } + public void setUseKeys(boolean useKeys) { + this.useKeys = useKeys; + } + public boolean getUseKeys() { + return useKeys; + } + public void setPostLogin(byte[] postLogin) { + this.postLogin = postLogin; + } + public byte[] getPostLogin() { + return postLogin; + } + public void setPubkeyId(long pubkeyId) { + this.pubkeyId = pubkeyId; + } + public long getPubkeyId() { + return pubkeyId; + } + public void setWantSession(boolean wantSession) { + this.wantSession = wantSession; + } + public boolean getWantSession() { + return wantSession; + } + public void setCompression(boolean compression) { + this.compression = compression; + } + public boolean getCompression() { + return compression; + } + + public String getDescription() { + String description = String.format("%s@%s", username, hostname); + + if (port != 22) + description += String.format(":%d", port); + + return description; + } + + public ContentValues getValues() { + ContentValues values = new ContentValues(); + + values.put(HostDatabase.FIELD_HOST_NICKNAME, nickname); + values.put(HostDatabase.FIELD_HOST_USERNAME, username); + values.put(HostDatabase.FIELD_HOST_HOSTNAME, hostname); + values.put(HostDatabase.FIELD_HOST_PORT, port); + values.put(HostDatabase.FIELD_HOST_HOSTKEYALGO, hostKeyAlgo); + values.put(HostDatabase.FIELD_HOST_HOSTKEY, hostKey); + values.put(HostDatabase.FIELD_HOST_LASTCONNECT, lastConnect); + values.put(HostDatabase.FIELD_HOST_COLOR, color); + values.put(HostDatabase.FIELD_HOST_USEKEYS, Boolean.toString(useKeys)); + values.put(HostDatabase.FIELD_HOST_POSTLOGIN, postLogin); + values.put(HostDatabase.FIELD_HOST_PUBKEYID, pubkeyId); + values.put(HostDatabase.FIELD_HOST_WANTSESSION, Boolean.toString(wantSession)); + values.put(HostDatabase.FIELD_HOST_COMPRESSION, Boolean.toString(compression)); + + return values; + } + + public boolean equals(Object o) { + if (!(o instanceof HostBean)) + return false; + + HostBean host = (HostBean)o; + + if (host.getNickname().equals(nickname) + && host.getUsername().equals(username) + && host.getHostname().equals(hostname) + && host.getPort() == port) + return true; + + return false; + } +} diff --git a/src/org/connectbot/bean/PortForwardBean.java b/src/org/connectbot/bean/PortForwardBean.java index ea344c9..6cbf6fa 100644 --- a/src/org/connectbot/bean/PortForwardBean.java +++ b/src/org/connectbot/bean/PortForwardBean.java @@ -27,6 +27,7 @@ import android.content.ContentValues; * */ public class PortForwardBean { + /* Database fields */ private long id = -1; private long hostId = -1; private String nickname = null; @@ -35,6 +36,7 @@ public class PortForwardBean { private String destAddr = null; private int destPort = -1; + /* Transient values */ private boolean enabled = false; private Object identifier = null; diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index b9dbcdf..d55ad5f 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -30,6 +30,7 @@ import java.util.List; import org.connectbot.R; import org.connectbot.TerminalView; +import org.connectbot.bean.HostBean; import org.connectbot.bean.PortForwardBean; import org.connectbot.util.HostDatabase; import org.connectbot.util.PubkeyDatabase; @@ -106,11 +107,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal protected final TerminalManager manager; - public final String nickname; - protected final String username; - public String postlogin = null; - private boolean wantSession = true; - private boolean compression = false; + public HostBean host; + //public final String nickname; + //protected final String username; + //public String postlogin = null; + //private boolean wantSession = true; + //private boolean compression = false; public final Connection connection; protected Session session; @@ -137,6 +139,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private boolean pubkeysExhausted = false; + private boolean authenticated = false; + + private boolean sessionOpen = false; + private boolean forcedSize = false; private int termWidth; private int termHeight; @@ -149,7 +155,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal KnownHosts hosts = manager.hostdb.getKnownHosts(); Boolean result; - switch(hosts.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey)) { + String matchName = String.format("%s:%d", hostname, port); + + switch(hosts.verifyHostkey(matchName, serverHostKeyAlgorithm, serverHostKey)) { case KnownHosts.HOSTKEY_IS_OK: return true; @@ -162,7 +170,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if(result == null) return false; if(result.booleanValue()) { // save this key in known database - manager.hostdb.saveKnownHost(hostname, serverHostKeyAlgorithm, serverHostKey); + manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); } return result.booleanValue(); @@ -180,7 +188,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if(result == null) return false; if(result.booleanValue()) { // save this key in known database - manager.hostdb.saveKnownHost(hostname, serverHostKeyAlgorithm, serverHostKey); + manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); } return result.booleanValue(); } @@ -198,17 +206,13 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal * launch thread to start SSH connection and handle any hostkey verification * and password authentication. */ - public TerminalBridge(final TerminalManager manager, final String nickname, final String username, final String hostname, final int port) throws Exception { + public TerminalBridge(final TerminalManager manager, final HostBean host) throws Exception { this.manager = manager; - this.nickname = nickname; - this.username = username; + this.host = host; this.emulation = manager.getEmulation(); this.scrollback = manager.getScrollback(); - this.postlogin = manager.getPostLogin(nickname); - this.wantSession = manager.getWantSession(nickname); - this.compression = manager.getCompression(nickname); // create prompt helper to relay password and hostkey requests up to gui this.promptHelper = new PromptHelper(this); @@ -251,14 +255,13 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.buffer.setCursorPosition(0, 0); // TODO Change this when hosts are beans as well - this.portForwards = manager.hostdb.getPortForwardsForHost(manager.hostdb.findHostByNickname(nickname)); + this.portForwards = manager.hostdb.getPortForwardsForHost(host); // 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.outputLine(String.format("Connecting to %s:%d", host.getHostname(), host.getPort())); + this.connection = new Connection(host.getHostname(), host.getPort()); this.connection.addConnectionMonitor(this); - this.connection.setCompression(compression); } public final static int AUTH_TRIES = 20; @@ -357,7 +360,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } } - return this.tryPublicKey(this.username, nickname, trileadKey); + return this.tryPublicKey(host.getUsername(), host.getNickname(), trileadKey); } @@ -371,7 +374,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public void handleAuthentication() { try { - if (connection.authenticateWithNone(username)) { + if (connection.authenticateWithNone(host.getUsername())) { finishConnection(); return; } @@ -382,11 +385,11 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine("Trying to authenticate"); try { - long pubkeyId = manager.hostdb.getPubkeyId(nickname); + long pubkeyId = host.getPubkeyId(); if (!pubkeysExhausted && pubkeyId != HostDatabase.PUBKEYID_NEVER && - connection.isAuthMethodAvailable(username, AUTH_PUBLICKEY)) { + connection.isAuthMethodAvailable(host.getUsername(), AUTH_PUBLICKEY)) { // if explicit pubkey defined for this host, then prompt for password as needed // otherwise just try all in-memory keys held in terminalmanager @@ -396,7 +399,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine("Attempting 'publickey' authentication with any in-memory SSH keys"); for(String nickname : manager.loadedPubkeys.keySet()) { Object trileadKey = manager.loadedPubkeys.get(nickname); - if(this.tryPublicKey(this.username, nickname, trileadKey)) { + if(this.tryPublicKey(host.getUsername(), nickname, trileadKey)) { finishConnection(); break; } @@ -414,20 +417,20 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } pubkeysExhausted = true; - } else if (connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { + } else if (connection.isAuthMethodAvailable(host.getUsername(), AUTH_PASSWORD)) { outputLine("Attempting 'password' authentication"); String password = promptHelper.requestStringPrompt("Password"); - if(connection.authenticateWithPassword(username, password)) { + if(connection.authenticateWithPassword(host.getUsername(), password)) { finishConnection(); } else { outputLine("Authentication method 'password' failed"); } - } else if(connection.isAuthMethodAvailable(username, AUTH_KEYBOARDINTERACTIVE)) { + } else if(connection.isAuthMethodAvailable(host.getUsername(), 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)) { + if(connection.authenticateWithKeyboardInteractive(host.getUsername(), TerminalBridge.this)) { finishConnection(); } else { outputLine("Authentication method 'keyboard-interactive' failed"); @@ -482,9 +485,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal }).start(); } - private boolean authenticated = false; - private boolean sessionOpen = false; - /** * Internal method to request actual PTY terminal once we've finished * authentication. If called before authenticated, it will just fail. @@ -502,7 +502,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } } - if (!wantSession) { + if (!host.getWantSession()) { outputLine("Session will not be started due to host preference."); return; } @@ -549,7 +549,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal setSessionOpen(true); // finally send any post-login string, if requested - injectString(postlogin); + byte[] postLogin = host.getPostLogin(); + if (postLogin != null) + injectString(new String(postLogin)); } catch (IOException e1) { Log.e(TAG, "Problem while trying to create PTY in finishConnection()", e1); diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index deeb0f2..78724f1 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -25,6 +25,7 @@ import java.util.LinkedList; import java.util.List; import org.connectbot.R; +import org.connectbot.bean.HostBean; import org.connectbot.util.HostDatabase; import org.connectbot.util.PubkeyDatabase; import org.connectbot.util.PubkeyUtils; @@ -56,7 +57,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public List bridges = new LinkedList(); public TerminalBridge defaultBridge = null; - public List disconnected = new LinkedList(); + public List disconnected = new LinkedList(); protected HashMap loadedPubkeys = new HashMap(); @@ -130,20 +131,19 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen /** * Open a new SSH session using the given parameters. */ - public void openConnection(String nickname, String hostname, String username, int port) throws Exception { + public void openConnection(HostBean host) throws Exception { // throw exception if terminal already open - if(this.findBridge(nickname) != null) { + if (this.findBridge(host) != null) { throw new Exception("Connection already open for that nickname"); } - TerminalBridge bridge = new TerminalBridge(this, nickname, username, hostname, port); + TerminalBridge bridge = new TerminalBridge(this, host); bridge.setOnDisconnectedListener(this); bridge.startConnection(); this.bridges.add(bridge); // also update database with new connected time - this.touchHost(nickname); - + this.touchHost(host); } public String getEmulation() { @@ -162,18 +162,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public boolean isSavingKeys() { return prefs.getBoolean(this.pref_memkeys, true); } - - public String getPostLogin(String nickname) { - return hostdb.getPostLogin(nickname); - } - - public boolean getWantSession(String nickname) { - return hostdb.getWantSession(nickname); - } - - public boolean getCompression(String nickname) { - return hostdb.getCompression(nickname); - } public String getKeyMode() { return prefs.getString(this.pref_keymode, getString(R.string.list_keymode_right)); // "Use right-side keys" @@ -189,24 +177,32 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen String hostname = uri.getHost(); int port = uri.getPort(); - this.openConnection(nickname, hostname, username, port); + HostBean host = hostdb.findHost(nickname, username, hostname, port); + + if (host == null) { + Log.d(TAG, String.format("Didn't find existing host (nickname=%s, username=%s, hostname=%s, port=%d)", + nickname, username, hostname, port)); + host = new HostBean(nickname, username, hostname, port); + } + + this.openConnection(host); } /** * Update the last-connected value for the given nickname by passing through * to {@link HostDatabase}. */ - protected void touchHost(String nickname) { - hostdb.touchHost(nickname); + protected void touchHost(HostBean host) { + hostdb.touchHost(host); } /** * Find the {@link TerminalBridge} with the given nickname. */ - public TerminalBridge findBridge(String nickname) { + public TerminalBridge findBridge(HostBean host) { // find the first active bridge with given nickname for(TerminalBridge bridge : bridges) { - if(bridge.nickname.equals(nickname)) + if (bridge.host.equals(host)) return bridge; } return null; @@ -229,7 +225,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public void onDisconnected(TerminalBridge bridge) { // remove this bridge from our list this.bridges.remove(bridge); - this.disconnected.add(bridge.nickname); + this.disconnected.add(bridge.host); // pass notification back up to gui if(this.disconnectHandler != null) diff --git a/src/org/connectbot/util/HostBinder.java b/src/org/connectbot/util/HostBinder.java deleted file mode 100644 index 39439c7..0000000 --- a/src/org/connectbot/util/HostBinder.java +++ /dev/null @@ -1,144 +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 . -*/ - -package org.connectbot.util; - -import org.connectbot.R; -import org.connectbot.service.TerminalManager; - -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.database.Cursor; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.SimpleCursorAdapter.ViewBinder; - -/** - * Binder used to help interpret a HostDatabase cursor into a ListView for - * display. Specifically, it controls the green "bulb" icon by checking against - * TerminalManager for status, and shows the last-connected time in a - * user-friendly format like "6 days ago." - * - * @author jsharkey - */ -public class HostBinder implements ViewBinder { - - protected final TerminalManager manager; - protected final ColorStateList red, green, blue; - protected int COL_NICKNAME = -1; - - public HostBinder(TerminalManager manager, Resources res) { - this.manager = manager; - this.red = res.getColorStateList(R.color.red); - this.green = res.getColorStateList(R.color.green); - this.blue = res.getColorStateList(R.color.blue); - - } - - 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 int getConnectedState(String nickname) { - // always disconnected if we dont have backend service - 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) { - - switch(view.getId()) { - case android.R.id.icon: - // set icon state based on status from backend service - ImageView icon = (ImageView)view; - - if(COL_NICKNAME == -1) - COL_NICKNAME = cursor.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME); - - String nickname = cursor.getString(COL_NICKNAME); - 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; - - case android.R.id.content: - // set background color correctly - String color = cursor.getString(columnIndex); - TextView text1 = (TextView)view.findViewById(android.R.id.text1), - text2 = (TextView)view.findViewById(android.R.id.text2); - - ColorStateList chosen = null; - if(HostDatabase.COLOR_RED.equals(color)) chosen = this.red; - if(HostDatabase.COLOR_GREEN.equals(color)) chosen = this.green; - if(HostDatabase.COLOR_BLUE.equals(color)) chosen = this.blue; - - if(chosen != null) { - // set color normally if not selected - text1.setTextColor(chosen); - text2.setTextColor(chosen); - } else { - // selected, so revert back to default black text - text1.setTextAppearance(view.getContext(), android.R.attr.textAppearanceLarge); - text2.setTextAppearance(view.getContext(), android.R.attr.textAppearanceSmall); - } - return true; - - case android.R.id.text2: - // correctly set last-connected time and status - long created = cursor.getLong(columnIndex); - long now = System.currentTimeMillis() / 1000; - - String nice = "never"; - if(created > 0) { - int minutes = (int)((now - created) / 60); - nice = view.getContext().getString(R.string.bind_minutes, minutes); - if(minutes >= 60) { - int hours = (minutes / 60); - nice = view.getContext().getString(R.string.bind_hours, hours); - if(hours >= 24) { - int days = (hours / 24); - nice = view.getContext().getString(R.string.bind_days, days); - } - } - } - - ((TextView)view).setText(nice); - return true; - } - - // otherwise fall through to other binding methods - return false; - - } - - -} diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index d935f69..978c9d7 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -21,6 +21,7 @@ package org.connectbot.util; import java.util.LinkedList; import java.util.List; +import org.connectbot.bean.HostBean; import org.connectbot.bean.PortForwardBean; import com.trilead.ssh2.KnownHosts; @@ -105,8 +106,8 @@ public class HostDatabase extends SQLiteOpenHelper { // insert a few sample hosts, none of which probably connect //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_GRAY, PUBKEYID_ANY); - this.createHost(db, "backup@example.net", "backup", "example.net", 22, COLOR_BLUE, PUBKEYID_ANY); + //this.createHost(db, "cron@server.example.com", "cron", "server.example.com", 22, COLOR_GRAY, PUBKEYID_ANY); + //this.createHost(db, "backup@example.net", "backup", "example.net", 22, COLOR_BLUE, PUBKEYID_ANY); db.execSQL("CREATE TABLE " + TABLE_PORTFORWARDS + " (_id INTEGER PRIMARY KEY, " @@ -154,196 +155,187 @@ public class HostDatabase extends SQLiteOpenHelper { * Touch a specific host to update its "last connected" field. * @param nickname Nickname field of host to update */ - public void touchHost(String nickname) { - + public void touchHost(HostBean host) { SQLiteDatabase db = this.getWritableDatabase(); long now = System.currentTimeMillis() / 1000; ContentValues values = new ContentValues(); values.put(FIELD_HOST_LASTCONNECT, now); - db.update(TABLE_HOSTS, values, FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }); - db.close(); - - } - - /** - * Find a specific host ID - * @param nickname Nickname field of host to find - */ - public long findHostByNickname(String nickname) { - SQLiteDatabase db = this.getReadableDatabase(); - long id = -1; - - Cursor c = db.query(TABLE_HOSTS, new String[] { "_id" }, - FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, - null, null, null); - - if (c != null && c.moveToFirst()) - id = c.getLong(0); + db.update(TABLE_HOSTS, values, "_id = ?", new String[] { String.valueOf(host.getId()) }); - c.close(); db.close(); - - return id; } /** - * Find a host's nickname in the database by its ID. - * @param hostId - * @return the host's nickname + * Create a new host using the given parameters. */ - public String findNicknameById(long hostId) { - SQLiteDatabase db = this.getReadableDatabase(); - String nickname = "unsaved host"; - - Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_NICKNAME }, - "_id = ?", new String[] { String.valueOf(hostId) }, - null, null, null); - - if (c != null && c.moveToFirst()) - nickname = c.getString(0); + public HostBean saveHost(HostBean host) { + SQLiteDatabase db = this.getWritableDatabase(); - c.close(); + long id = db.insert(TABLE_HOSTS, null, host.getValues()); db.close(); - - return nickname; - } - /** - * Create a new host using the given parameters, and return its new - * _id value. - */ - public long createHost(SQLiteDatabase db, String nickname, String username, String hostname, int port, String color, long pubkeyId) { - // create and insert new host - - if(db == null) db = this.getWritableDatabase(); - - ContentValues values = new ContentValues(); - values.put(FIELD_HOST_NICKNAME, nickname); - values.put(FIELD_HOST_USERNAME, username); - values.put(FIELD_HOST_HOSTNAME, hostname); - values.put(FIELD_HOST_PORT, port); - values.put(FIELD_HOST_LASTCONNECT, 0); - values.put(FIELD_HOST_USEKEYS, Boolean.toString(true)); - if(color != null) - values.put(FIELD_HOST_COLOR, color); - values.put(FIELD_HOST_PUBKEYID, pubkeyId); - values.put(FIELD_HOST_WANTSESSION, Boolean.toString(true)); - - return db.insert(TABLE_HOSTS, null, values); + host.setId(id); + return host; } /** * Delete a specific host by its _id value. */ - public void deleteHost(long id) { + public void deleteHost(HostBean host) { + if (host.getId() < 0) + return; SQLiteDatabase db = this.getWritableDatabase(); - db.delete(TABLE_HOSTS, "_id = ?", new String[] { Long.toString(id) }); - + db.delete(TABLE_HOSTS, "_id = ?", new String[] { String.valueOf(host.getId()) }); + db.close(); } /** * Return a cursor that contains information about all known hosts. * @param sortColors If true, sort by color, otherwise sort by nickname. */ - public Cursor allHosts(boolean sortColors) { - + public List getHosts(boolean sortColors) { String sortField = sortColors ? FIELD_HOST_COLOR : FIELD_HOST_NICKNAME; - SQLiteDatabase db = this.getReadableDatabase(); - return db.query(TABLE_HOSTS, new String[] { "_id", FIELD_HOST_NICKNAME, - FIELD_HOST_USERNAME, FIELD_HOST_HOSTNAME, FIELD_HOST_PORT, - FIELD_HOST_LASTCONNECT, FIELD_HOST_COLOR }, - null, null, null, null, sortField + " ASC"); - } - - /** - * Find the post-login command string for the given nickname. - */ - public String getPostLogin(String nickname) { - String result = null; + List hosts = new LinkedList(); - SQLiteDatabase db = this.getReadableDatabase(); - Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_POSTLOGIN }, - FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null); + Cursor c = db.query(TABLE_HOSTS, null, null, null, null, null, sortField + " ASC"); + + int COL_ID = c.getColumnIndexOrThrow("_id"), + COL_NICKNAME = c.getColumnIndexOrThrow(FIELD_HOST_NICKNAME), + COL_USERNAME = c.getColumnIndexOrThrow(FIELD_HOST_USERNAME), + COL_HOSTNAME = c.getColumnIndexOrThrow(FIELD_HOST_HOSTNAME), + COL_PORT = c.getColumnIndexOrThrow(FIELD_HOST_PORT), + COL_LASTCONNECT = c.getColumnIndexOrThrow(FIELD_HOST_LASTCONNECT), + COL_COLOR = c.getColumnIndexOrThrow(FIELD_HOST_COLOR), + COL_USEKEYS = c.getColumnIndexOrThrow(FIELD_HOST_USEKEYS), + COL_POSTLOGIN = c.getColumnIndexOrThrow(FIELD_HOST_POSTLOGIN), + COL_PUBKEYID = c.getColumnIndexOrThrow(FIELD_HOST_PUBKEYID), + COL_WANTSESSION = c.getColumnIndexOrThrow(FIELD_HOST_WANTSESSION), + COL_COMPRESSION = c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION); - if (c == null || !c.moveToFirst()) { - result = null; - } else { - result = c.getString(c.getColumnIndexOrThrow(FIELD_HOST_POSTLOGIN)); + while (c.moveToNext()) { + HostBean host = new HostBean(); + + host.setId(c.getLong(COL_ID)); + host.setNickname(c.getString(COL_NICKNAME)); + host.setUsername(c.getString(COL_USERNAME)); + host.setHostname(c.getString(COL_HOSTNAME)); + host.setPort(c.getInt(COL_PORT)); + host.setLastConnect(c.getLong(COL_LASTCONNECT)); + host.setColor(c.getString(COL_COLOR)); + host.setUseKeys(Boolean.valueOf(c.getString(COL_USEKEYS))); + host.setPostLogin(c.getBlob(COL_POSTLOGIN)); + host.setPubkeyId(c.getLong(COL_PUBKEYID)); + host.setWantSession(Boolean.valueOf(c.getString(COL_WANTSESSION))); + host.setCompression(Boolean.valueOf(c.getString(COL_COMPRESSION))); + + hosts.add(host); } c.close(); db.close(); - - return result; + + return hosts; } - + /** - * Check whether a host should have a shell session started. - * @param nickname Nick name of host to check - * @return true if host should have a shell session started + * @param nickname + * @param username + * @param hostname + * @param port + * @return */ - public boolean getWantSession(String nickname) { - Boolean result = true; - + public HostBean findHost(String nickname, String username, String hostname, + int port) { SQLiteDatabase db = this.getReadableDatabase(); - Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_WANTSESSION }, - FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null); - if(c == null || !c.moveToFirst()) { - result = true; - } else { - result = Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_WANTSESSION))); + + Cursor c = db.query(TABLE_HOSTS, null, + FIELD_HOST_NICKNAME + " = ? AND " + + FIELD_HOST_USERNAME + " = ? AND " + + FIELD_HOST_HOSTNAME + " = ? AND " + + FIELD_HOST_PORT + " = ?", + new String[] { nickname, username, hostname, String.valueOf(port) }, + null, null, null); + + HostBean host = null; + + if (c != null && c.moveToFirst()) { + host = createHostBean(c); } c.close(); db.close(); - return result; + return host; } /** - * Check whether a host should have compression enabled. - * @param nickname Nick name of host to check - * @return true if host should have compression enabled + * @param hostId + * @return */ - public boolean getCompression(String nickname) { - Boolean result = false; - + public HostBean findHostById(long hostId) { SQLiteDatabase db = this.getReadableDatabase(); - Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_COMPRESSION }, - FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null); - if (c != null && c.moveToFirst()) - result = Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION))); + Cursor c = db.query(TABLE_HOSTS, null, + "_id = ?", new String[] { String.valueOf(hostId) }, + null, null, null); + + HostBean host = null; + + if (c != null && c.moveToFirst()) { + host = createHostBean(c); + } c.close(); db.close(); - return result; + return host; + } + + private HostBean createHostBean(Cursor c) { + HostBean host = new HostBean(); + + host.setId(c.getLong(c.getColumnIndexOrThrow("_id"))); + host.setNickname(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_NICKNAME))); + host.setUsername(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_USERNAME))); + host.setHostname(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_HOSTNAME))); + host.setPort(c.getInt(c.getColumnIndexOrThrow(FIELD_HOST_PORT))); + host.setLastConnect(c.getLong(c.getColumnIndexOrThrow(FIELD_HOST_LASTCONNECT))); + host.setColor(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_COLOR))); + host.setUseKeys(Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_USEKEYS)))); + host.setPostLogin(c.getBlob(c.getColumnIndexOrThrow(FIELD_HOST_POSTLOGIN))); + host.setPubkeyId(c.getLong(c.getColumnIndexOrThrow(FIELD_HOST_PUBKEYID))); + host.setWantSession(Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_WANTSESSION)))); + host.setCompression(Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION)))); + + return host; } /** * Record the given hostkey into database under this nickname. * @param hostname + * @param port * @param hostkeyalgo * @param hostkey */ - public void saveKnownHost(String hostname, String hostkeyalgo, byte[] hostkey) { - + public void saveKnownHost(String hostname, int port, 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 }); + db.update(TABLE_HOSTS, values, + FIELD_HOST_HOSTNAME + " = ? AND " + FIELD_HOST_PORT + " = ?", + new String[] { hostname, String.valueOf(port) }); Log.d(TAG, String.format("Finished saving hostkey information for '%s'", hostname)); + db.close(); } /** @@ -355,66 +347,38 @@ public class HostDatabase extends SQLiteOpenHelper { 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; + FIELD_HOST_PORT, FIELD_HOST_HOSTKEYALGO, FIELD_HOST_HOSTKEY }, + null, null, null, null, null); + + if (c != null) { + + int COL_HOSTNAME = c.getColumnIndexOrThrow(FIELD_HOST_HOSTNAME), + COL_PORT = c.getColumnIndexOrThrow(FIELD_HOST_PORT), + COL_HOSTKEYALGO = c.getColumnIndexOrThrow(FIELD_HOST_HOSTKEYALGO), + COL_HOSTKEY = c.getColumnIndexOrThrow(FIELD_HOST_HOSTKEY); - try { - known.addHostkey(new String[] { hostname }, hostkeyalgo, hostkey); - } catch(Exception e) { - Log.e(TAG, "Problem while adding a known host from database", e); + while(c.moveToNext()) { + String hostname = c.getString(COL_HOSTNAME), + hostkeyalgo = c.getString(COL_HOSTKEYALGO); + int port = c.getInt(COL_PORT); + 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[] { String.format("%s:%d", hostname, port) }, hostkeyalgo, hostkey); + } catch(Exception e) { + Log.e(TAG, "Problem while adding a known host from database", e); + } + } - } - return known; - } - - /** - * Find the pubkey to use for a given nickname. - * @param nickname of host - * @return pubkey ID in database - */ - public long getPubkeyId(String nickname) { - long result = PUBKEYID_ANY; - - SQLiteDatabase db = this.getReadableDatabase(); - Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_PUBKEYID }, - FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null); - - if (c != null && c.moveToFirst()) - result = c.getLong(0); - - return result; - } - - /** - * Set which public key to use for a particular host. - * @param hostId host ID in database - * @param pubkeyId public key ID in databae - */ - public void setPubkeyId(long hostId, long pubkeyId) { - SQLiteDatabase db = this.getWritableDatabase(); - - ContentValues values = new ContentValues(); - values.put(FIELD_HOST_PUBKEYID, pubkeyId); - - db.update(TABLE_HOSTS, values, "_id = ?", new String[] { String.valueOf(hostId) }); + c.close(); db.close(); - - Log.d(TAG, String.format("Updated host id %d to use pubkey id %d", hostId, pubkeyId)); + + return known; } /** @@ -441,23 +405,23 @@ public class HostDatabase extends SQLiteOpenHelper { /** * Returns a list of all the port forwards associated with a particular host ID. - * @param hostId ID of host + * @param host the host for which we want the port forward list * @return port forwards associated with host ID */ - public List getPortForwardsForHost(long hostId) { + public List getPortForwardsForHost(HostBean host) { SQLiteDatabase db = this.getReadableDatabase(); List portForwards = new LinkedList(); Cursor c = db.query(TABLE_PORTFORWARDS, new String[] { "_id", FIELD_PORTFORWARD_NICKNAME, FIELD_PORTFORWARD_TYPE, FIELD_PORTFORWARD_SOURCEPORT, FIELD_PORTFORWARD_DESTADDR, FIELD_PORTFORWARD_DESTPORT }, - FIELD_PORTFORWARD_HOSTID + " = ?", new String[] { String.valueOf(hostId) }, + FIELD_PORTFORWARD_HOSTID + " = ?", new String[] { String.valueOf(host.getId()) }, null, null, null); while (c.moveToNext()) { PortForwardBean pfb = new PortForwardBean( c.getInt(0), - hostId, + host.getId(), c.getString(1), c.getString(2), c.getInt(3), @@ -472,7 +436,6 @@ public class HostDatabase extends SQLiteOpenHelper { return portForwards; } - /** * Update the parameters of a port forward in the database. * @param pfb {@link PortForwardBean} to save -- cgit v1.2.3