diff options
Diffstat (limited to 'src')
30 files changed, 1439 insertions, 1439 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index fd9dbcc..ab06d83 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -71,19 +71,19 @@ public class ConsoleActivity extends Activity { public final static String TAG = ConsoleActivity.class.toString(); protected static final int REQUEST_EDIT = 1; - + protected ViewFlipper flip = null; protected TerminalManager bound = null; protected LayoutInflater inflater = null; - - private SharedPreferences prefs = null; - + + private SharedPreferences prefs = null; + private PowerManager.WakeLock wakelock = null; - + private String PREF_KEEPALIVE = null; - + protected Uri requested; - + protected ClipboardManager clipboard; private RelativeLayout stringPromptGroup; protected EditText stringPrompt; @@ -93,35 +93,35 @@ public class ConsoleActivity extends Activity { private Button booleanYes, booleanNo; private TextView empty; - + private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out; - + private MenuItem disconnect, copy, paste, portForward, resize; - + protected TerminalBridge copySource = null; private int lastTouchRow, lastTouchCol; - + private ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { bound = ((TerminalManager.TerminalBinder) service).getService(); - + // let manager know about our event handling services bound.disconnectHandler = disconnectHandler; - + Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size())); - + // clear out any existing bridges and record requested index flip.removeAllViews(); String requestedNickname = (requested != null) ? requested.getFragment() : null; int requestedIndex = 0; - + // first check if we need to create a new session for requested boolean found = false; for(TerminalBridge bridge : bound.bridges) { if(bridge.host.getNickname().equals(requestedNickname)) found = true; } - + // If we didn't find the requested connection, try opening it if(!found) { try { @@ -131,35 +131,35 @@ public class ConsoleActivity extends Activity { Log.e(TAG, "Problem while trying to create new requested bridge from URI", e); } } - + // create views for all bridges on this service for(TerminalBridge bridge : bound.bridges) { - + // let them know about our prompt handler services bridge.promptHelper.setHandler(promptHandler); bridge.refreshKeymode(); - + // inflate each terminal view RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false); - + // set the terminal overlay text TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay); overlay.setText(bridge.host.getNickname()); - + // and add our terminal view control, using index to place behind overlay TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge); terminal.setId(R.id.console_flip); view.addView(terminal, 0); - + // finally attach to the flipper flip.addView(view); - + // check to see if this bridge was requested if(bridge.host.getNickname().equals(requestedNickname)) requestedIndex = flip.getChildCount() - 1; - + } - + try { // show the requested bridge if found, also fade out overlay flip.setDisplayedChild(requestedIndex); @@ -167,21 +167,21 @@ public class ConsoleActivity extends Activity { } catch (NullPointerException npe) { Log.d(TAG, "View went away when we were about to display it", npe); } - + updatePromptVisible(); updateEmptyVisible(); - + } - + public void onServiceDisconnected(ComponentName className) { // tell each bridge to forget about our prompt handler for(TerminalBridge bridge : bound.bridges) bridge.promptHelper.setHandler(null); - + flip.removeAllViews(); updateEmptyVisible(); bound = null; - + } }; @@ -201,7 +201,7 @@ public class ConsoleActivity extends Activity { // someone below us requested to display a password dialog // they are sending nickname and requested TerminalBridge bridge = (TerminalBridge)msg.obj; - + if (bridge.isAwaitingClose()) closeBridge(bridge); } @@ -213,18 +213,18 @@ public class ConsoleActivity extends Activity { private void closeBridge(TerminalBridge bridge) { for(int i = 0; i < flip.getChildCount(); i++) { View child = flip.getChildAt(i).findViewById(R.id.console_flip); - + if (!(child instanceof TerminalView)) continue; - + TerminalView terminal = (TerminalView) child; - + if (terminal.bridge.equals(bridge)) { // we've found the terminal to remove // shift something into its place if currently visible if(flip.getDisplayedChild() == i) shiftLeft(); flip.removeViewAt(i); - + /* TODO Remove this workaround when ViewFlipper is fixed to listen * to view removals. Android Issue 1784 */ @@ -232,18 +232,18 @@ public class ConsoleActivity extends Activity { if (flip.getDisplayedChild() >= numChildren && numChildren > 0) flip.setDisplayedChild(numChildren - 1); - + updateEmptyVisible(); break; } } - + // If we just closed the last bridge, go back to the previous activity. if (flip.getChildCount() == 0) { finish(); } } - + // TODO review use (apparently unused) protected void createPortForward(TerminalView target, String nickname, String type, String source, String dest) { String summary = getString(R.string.portforward_problem); @@ -251,7 +251,7 @@ public class ConsoleActivity extends Activity { long hostId = target.bridge.host.getId(); PortForwardBean pfb = new PortForwardBean(hostId, nickname, type, source, dest); - + target.bridge.addPortForward(pfb); if (target.bridge.enablePortForward(pfb)) { summary = getString(R.string.portforward_done); @@ -259,23 +259,23 @@ public class ConsoleActivity extends Activity { } catch(Exception e) { Log.e(TAG, "Problem trying to create portForward", e); } - + Toast.makeText(ConsoleActivity.this, summary, Toast.LENGTH_LONG).show(); } - + protected View findCurrentView(int id) { View view = flip.getCurrentView(); if(view == null) return null; return view.findViewById(id); } - + // TODO review use (apparently unused) protected HostBean getCurrentHost() { View view = findCurrentView(R.id.console_flip); if(!(view instanceof TerminalView)) return null; return ((TerminalView)view).bridge.host; } - + protected PromptHelper getCurrentPromptHelper() { View view = findCurrentView(R.id.console_flip); if(!(view instanceof TerminalView)) return null; @@ -288,20 +288,20 @@ public class ConsoleActivity extends Activity { } @Override - public void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); - + this.setContentView(R.layout.act_console); - + clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); prefs = PreferenceManager.getDefaultSharedPreferences(this); - + // hide status bar if requested by user if (prefs.getBoolean(getString(R.string.pref_fullscreen), false)) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } - + // request a forced orientation if requested by user String rotate = prefs.getString(getString(R.string.pref_rotation), getString(R.string.list_rotation_land)); if(getString(R.string.list_rotation_land).equals(rotate)) { @@ -309,33 +309,33 @@ public class ConsoleActivity extends Activity { } else if (getString(R.string.list_rotation_port).equals(rotate)) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } - + // TODO find proper way to disable volume key beep if it exists. setVolumeControlStream(AudioManager.STREAM_MUSIC); - - PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE); + + PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE); wakelock = manager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG); - + PREF_KEEPALIVE = getResources().getString(R.string.pref_keepalive); // handle requested console from incoming intent requested = getIntent().getData(); inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - + flip = (ViewFlipper)findViewById(R.id.console_flip); empty = (TextView)findViewById(android.R.id.empty); - + stringPromptGroup = (RelativeLayout) findViewById(R.id.console_password_group); stringPrompt = (EditText)findViewById(R.id.console_password); stringPrompt.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_UP) return false; if(keyCode != KeyEvent.KEYCODE_ENTER) return false; - + // pass collected password down to current terminal String value = stringPrompt.getText().toString(); - + PromptHelper helper = getCurrentPromptHelper(); if(helper == null) return false; helper.setResponse(value); @@ -347,10 +347,10 @@ public class ConsoleActivity extends Activity { return true; } }); - + booleanPromptGroup = (RelativeLayout) findViewById(R.id.console_boolean_group); booleanPrompt = (TextView)findViewById(R.id.console_prompt); - + booleanYes = (Button)findViewById(R.id.console_prompt_yes); booleanYes.setOnClickListener(new OnClickListener() { public void onClick(View v) { @@ -360,7 +360,7 @@ public class ConsoleActivity extends Activity { hideAllPrompts(); } }); - + booleanNo = (Button)findViewById(R.id.console_prompt_no); booleanNo.setOnClickListener(new OnClickListener() { public void onClick(View v) { @@ -370,23 +370,23 @@ public class ConsoleActivity extends Activity { hideAllPrompts(); } }); - + // preload animations for terminal switching slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); slide_right_in = AnimationUtils.loadAnimation(this, R.anim.slide_right_in); slide_right_out = AnimationUtils.loadAnimation(this, R.anim.slide_right_out); - + fade_out = AnimationUtils.loadAnimation(this, R.anim.fade_out); fade_stay_hidden = AnimationUtils.loadAnimation(this, R.anim.fade_stay_hidden); - + // detect fling gestures to switch between terminals - final GestureDetector detect = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { + final GestureDetector detect = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { private float totalY = 0; - + @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - + float distx = e2.getRawX() - e1.getRawX(); float disty = e2.getRawY() - e1.getRawY(); int goalwidth = flip.getWidth() / 2; @@ -398,25 +398,25 @@ public class ConsoleActivity extends Activity { shiftRight(); return true; } - + if(distx < -goalwidth) { shiftLeft(); return true; } - + } - + return false; } - - + + @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - + // if copying, then ignore if (copySource != null && copySource.isSelectingForCopy()) return false; - + if (e1 == null || e2 == null) return false; @@ -424,10 +424,10 @@ public class ConsoleActivity extends Activity { if(e2.getAction() == MotionEvent.ACTION_UP) { totalY = 0; } - + // activate consider if within x tolerance if(Math.abs(e1.getX() - e2.getX()) < ViewConfiguration.getTouchSlop() * 4) { - + View flip = findCurrentView(R.id.console_flip); if(flip == null) return false; TerminalView terminal = (TerminalView)flip; @@ -436,7 +436,7 @@ public class ConsoleActivity extends Activity { // accumulate distance that doesn't trigger immediate scroll totalY += distanceY; int moved = (int)(totalY / terminal.bridge.charHeight); - + // consume as scrollback only if towards right half of screen if (e2.getX() > flip.getWidth() / 2.0) { if(moved != 0) { @@ -458,29 +458,29 @@ public class ConsoleActivity extends Activity { totalY = 0; return true; } - + } - + } - + return false; } }); - + flip.setLongClickable(true); flip.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { - + // when copying, highlight the area if (copySource != null && copySource.isSelectingForCopy()) { int row = (int)Math.floor(event.getY() / copySource.charHeight); int col = (int)Math.floor(event.getX() / copySource.charWidth); - + SelectionArea area = copySource.getSelectionArea(); - + switch(event.getAction()) { case MotionEvent.ACTION_DOWN: // recording starting area @@ -498,10 +498,10 @@ public class ConsoleActivity extends Activity { */ if (row == lastTouchRow && col == lastTouchCol) return true; - + // if the user moves, start the selection for other corner area.finishSelectingOrigin(); - + // update selected area area.setBottom(row); area.setRight(col); @@ -517,14 +517,14 @@ public class ConsoleActivity extends Activity { area.getTop() == area.getBottom()) { return true; } - + // copy selected area to clipboard String copiedText = area.copyFrom(copySource.buffer); - + clipboard.setText(copiedText); Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, copiedText.length()), Toast.LENGTH_LONG).show(); // fall through to clear state - + case MotionEvent.ACTION_CANCEL: // make sure we clear any highlighted area area.reset(); @@ -532,36 +532,36 @@ public class ConsoleActivity extends Activity { copySource.redraw(); return true; } - - + + } - + // pass any touch events back to detector return detect.onTouchEvent(event); } - + }); - + } - + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - + final View view = findCurrentView(R.id.console_flip); final boolean activeTerminal = (view instanceof TerminalView); boolean authenticated = false; boolean sessionOpen = false; boolean disconnected = false; - + if (activeTerminal) { authenticated = ((TerminalView) view).bridge.isAuthenticated(); sessionOpen = ((TerminalView) view).bridge.isSessionOpen(); disconnected = ((TerminalView) view).bridge.isDisconnected(); } - - + + disconnect = menu.add(R.string.console_menu_disconnect); if (!sessionOpen && disconnected) disconnect.setTitle(R.string.console_menu_close); @@ -575,7 +575,7 @@ public class ConsoleActivity extends Activity { return true; } }); - + copy = menu.add(R.string.console_menu_copy); copy.setIcon(android.R.drawable.ic_menu_set_as); copy.setEnabled(activeTerminal); @@ -583,22 +583,22 @@ public class ConsoleActivity extends Activity { public boolean onMenuItemClick(MenuItem item) { // mark as copying and reset any previous bounds copySource = ((TerminalView)view).bridge; - + SelectionArea area = copySource.getSelectionArea(); area.reset(); area.setBounds(copySource.buffer.getColumns(), copySource.buffer.getRows()); - + copySource.setSelectingForCopy(true); - + // Make sure we show the initial selection copySource.redraw(); - + Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show(); return true; } }); - + paste = menu.add(R.string.console_menu_paste); paste.setIcon(android.R.drawable.ic_menu_edit); paste.setEnabled(clipboard.hasText() && activeTerminal && authenticated); @@ -606,7 +606,7 @@ public class ConsoleActivity extends Activity { public boolean onMenuItemClick(MenuItem item) { // force insert of clipboard text into current console TerminalView terminal = (TerminalView)view; - + // pull string from clipboard and generate all events to force down String clip = clipboard.getText().toString(); terminal.bridge.injectString(clip); @@ -614,8 +614,8 @@ public class ConsoleActivity extends Activity { return true; } }); - - + + portForward = menu.add(R.string.console_menu_portforwards); portForward.setIcon(android.R.drawable.ic_menu_manage); portForward.setEnabled(authenticated); @@ -627,14 +627,14 @@ public class ConsoleActivity extends Activity { return true; } }); - + resize = menu.add(R.string.console_menu_resize); resize.setIcon(android.R.drawable.ic_menu_crop); resize.setEnabled(activeTerminal && sessionOpen); resize.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { final TerminalView terminal = (TerminalView)view; - + final View resizeView = inflater.inflate(R.layout.dia_resize, null, false); new AlertDialog.Builder(ConsoleActivity.this) .setView(resizeView) @@ -642,22 +642,22 @@ public class ConsoleActivity extends Activity { public void onClick(DialogInterface dialog, int which) { int width = Integer.parseInt(((EditText)resizeView.findViewById(R.id.width)).getText().toString()); int height = Integer.parseInt(((EditText)resizeView.findViewById(R.id.height)).getText().toString()); - + terminal.forceSize(width, height); } }).setNegativeButton(android.R.string.cancel, null).create().show(); - + return true; } }); return true; } - + @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - + final View view = findCurrentView(R.id.console_flip); boolean activeTerminal = (view instanceof TerminalView); boolean authenticated = false; @@ -668,7 +668,7 @@ public class ConsoleActivity extends Activity { sessionOpen = ((TerminalView)view).bridge.isSessionOpen(); disconnected = ((TerminalView)view).bridge.isDisconnected(); } - + disconnect.setEnabled(activeTerminal); if (sessionOpen || !disconnected) disconnect.setTitle(R.string.console_menu_disconnect); @@ -681,22 +681,22 @@ public class ConsoleActivity extends Activity { return true; } - + @Override - public void onStart() { + public void onStart() { super.onStart(); // connect with manager service to find all bridges // when connected it will insert all views - bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); - // make sure we dont let the screen fall asleep + // make sure we dont let the screen fall asleep // this also keeps the wifi chipset from disconnecting us if(wakelock != null && prefs.getBoolean(PREF_KEEPALIVE, true)) wakelock.acquire(); } - + @Override public void onStop() { super.onStop(); @@ -705,13 +705,13 @@ public class ConsoleActivity extends Activity { // allow the screen to dim and fall asleep if(wakelock != null && wakelock.isHeld()) wakelock.release(); - + } - + protected void shiftLeft() { View overlay; boolean shouldAnimate = flip.getChildCount() > 1; - + // Only show animation if there is something else to go to. if (shouldAnimate) { // keep current overlay from popping up again @@ -723,19 +723,19 @@ public class ConsoleActivity extends Activity { flip.setOutAnimation(slide_left_out); flip.showNext(); } - + ConsoleActivity.this.updateDefault(); - + if (shouldAnimate) { // show overlay on new slide and start fade overlay = findCurrentView(R.id.terminal_overlay); if (overlay != null) overlay.startAnimation(fade_out); } - + updatePromptVisible(); } - + protected void shiftRight() { View overlay; boolean shouldAnimate = flip.getChildCount() > 1; @@ -751,7 +751,7 @@ public class ConsoleActivity extends Activity { flip.setOutAnimation(slide_right_out); flip.showPrevious(); } - + ConsoleActivity.this.updateDefault(); if (shouldAnimate) { @@ -778,13 +778,13 @@ public class ConsoleActivity extends Activity { if(bound == null) return; bound.defaultBridge = terminal.bridge; } - + protected void updateEmptyVisible() { // update visibility of empty status message empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); } - /** + /** * Show any prompts requested by the currently visible {@link TerminalView}. */ protected void updatePromptVisible() { @@ -798,19 +798,19 @@ public class ConsoleActivity extends Activity { // we dont have an active view, so hide any prompts return; } - + PromptHelper prompt = ((TerminalView)view).bridge.promptHelper; if(String.class.equals(prompt.promptRequested)) { stringPromptGroup.setVisibility(View.VISIBLE); stringPrompt.setText(""); stringPrompt.setHint(prompt.promptHint); stringPrompt.requestFocus(); - + } else if(Boolean.class.equals(prompt.promptRequested)) { booleanPromptGroup.setVisibility(View.VISIBLE); booleanPrompt.setText(prompt.promptHint); booleanYes.requestFocus(); - + } else { hideAllPrompts(); view.requestFocus(); diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java index 5d5a4de..9408cb0 100644 --- a/src/org/connectbot/GeneratePubkeyActivity.java +++ b/src/org/connectbot/GeneratePubkeyActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -57,7 +57,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere public final static String TAG = GeneratePubkeyActivity.class.toString(); final static int DEFAULT_BITS = 1024; - + private LayoutInflater inflater = null; private EditText nickname; @@ -68,80 +68,80 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere private Button save; private Dialog entropyDialog; private ProgressDialog progress; - + private EditText password1, password2; - + private String keyType = PubkeyDatabase.KEY_TYPE_RSA; private int minBits = 768; private int bits = DEFAULT_BITS; - + private byte[] entropy; - + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - + setContentView(R.layout.act_generatepubkey); - + nickname = (EditText) findViewById(R.id.nickname); - + keyTypeGroup = (RadioGroup) findViewById(R.id.key_type); - + bitsText = (EditText) findViewById(R.id.bits); bitsSlider = (SeekBar) findViewById(R.id.bits_slider); - + password1 = (EditText) findViewById(R.id.password1); password2 = (EditText) findViewById(R.id.password2); - + unlockAtStartup = (CheckBox) findViewById(R.id.unlock_at_startup); - + save = (Button) findViewById(R.id.save); - + inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); nickname.addTextChangedListener(textChecker); password1.addTextChangedListener(textChecker); password2.addTextChangedListener(textChecker); - + keyTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(RadioGroup group, int checkedId) { if (checkedId == R.id.rsa) { minBits = 768; - + bitsSlider.setEnabled(true); bitsSlider.setProgress(DEFAULT_BITS - minBits); - + bitsText.setText(String.valueOf(DEFAULT_BITS)); bitsText.setEnabled(true); - + keyType = PubkeyDatabase.KEY_TYPE_RSA; } else if (checkedId == R.id.dsa) { // DSA keys can only be 1024 bits - + bitsSlider.setEnabled(false); bitsSlider.setProgress(DEFAULT_BITS - minBits); - + bitsText.setText(String.valueOf(DEFAULT_BITS)); bitsText.setEnabled(false); - + keyType = PubkeyDatabase.KEY_TYPE_DSA; - } + } } }); - + bitsSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { // Stay evenly divisible by 8 because it looks nicer to have // 2048 than 2043 bits. - + int leftover = progress % 8; - + if (leftover > 0) progress += 8 - leftover; - + bits = minBits + progress; bitsText.setText(String.valueOf(bits)); } @@ -154,7 +154,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere // We don't care about the stop. } }); - + bitsText.setOnFocusChangeListener(new OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { @@ -168,16 +168,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere bits = DEFAULT_BITS; bitsText.setText(String.valueOf(bits)); } - + bitsSlider.setProgress(bits - minBits); } } }); - + save.setOnClickListener(new OnClickListener() { public void onClick(View view) { GeneratePubkeyActivity.this.save.setEnabled(false); - + GeneratePubkeyActivity.this.startEntropyGather(); } }); @@ -186,36 +186,36 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere private void checkEntries() { boolean allowSave = true; - + if (!password1.getText().toString().equals(password2.getText().toString())) allowSave = false; - - if (nickname.getText().length() == 0) + + if (nickname.getText().length() == 0) allowSave = false; - + save.setEnabled(allowSave); } - + private void startEntropyGather() { final View entropyView = inflater.inflate(R.layout.dia_gatherentropy, null, false); ((EntropyView)entropyView.findViewById(R.id.entropy)).addOnEntropyGatheredListener(GeneratePubkeyActivity.this); entropyDialog = new EntropyDialog(GeneratePubkeyActivity.this, entropyView); entropyDialog.show(); } - + public void onEntropyGathered(byte[] entropy) { // For some reason the entropy dialog was aborted, exit activity if (entropy == null) { finish(); return; } - + this.entropy = entropy.clone(); - + Log.d(TAG, "entropy gathered; attemping to generate key..."); startKeyGen(); } - + private void startKeyGen() { progress = new ProgressDialog(GeneratePubkeyActivity.this); progress.setMessage(GeneratePubkeyActivity.this.getResources().getText(R.string.pubkey_generating)); @@ -225,38 +225,38 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere new Thread(mKeyGen).start(); } - + private Handler handler = new Handler() { public void handleMessage(Message msg) { progress.dismiss(); GeneratePubkeyActivity.this.finish(); } }; - + final private Runnable mKeyGen = new Runnable() { public void run() { try { boolean encrypted = false; - + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - + random.setSeed(entropy); - + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(keyType); - + keyPairGen.initialize(bits, random); - + KeyPair pair = keyPairGen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - + String secret = password1.getText().toString(); if (secret.length() > 0) encrypted = true; - + Log.d(TAG, "private: " + PubkeyUtils.formatKey(priv)); Log.d(TAG, "public: " + PubkeyUtils.formatKey(pub)); - + PubkeyBean pubkey = new PubkeyBean(); pubkey.setNickname(nickname.getText().toString()); pubkey.setType(keyType); @@ -264,7 +264,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere pubkey.setPublicKey(PubkeyUtils.getEncodedPublic(pub)); pubkey.setEncrypted(encrypted); pubkey.setStartup(unlockAtStartup.isChecked()); - + PubkeyDatabase pubkeydb = new PubkeyDatabase(GeneratePubkeyActivity.this); pubkeydb.savePubkey(pubkey); pubkeydb.close(); @@ -273,12 +273,12 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere e.printStackTrace(); } - + handler.sendEmptyMessage(0); } }; - + final private TextWatcher textChecker = new TextWatcher() { public void afterTextChanged(Editable s) {} diff --git a/src/org/connectbot/HelpActivity.java b/src/org/connectbot/HelpActivity.java index c04127f..24ff2b4 100644 --- a/src/org/connectbot/HelpActivity.java +++ b/src/org/connectbot/HelpActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -35,7 +35,7 @@ import android.widget.LinearLayout; */ public class HelpActivity extends Activity { public final static String TAG = HelpActivity.class.toString(); - + public final static String HELPDIR = "help"; public final static String SUFFIX = ".html"; @@ -43,11 +43,11 @@ public class HelpActivity extends Activity { public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.act_help); - + this.setTitle(String.format("%s: %s", getResources().getText(R.string.app_name), getResources().getText(R.string.title_help))); - + AssetManager am = this.getAssets(); LinearLayout content = (LinearLayout)this.findViewById(R.id.topics); @@ -57,7 +57,7 @@ public class HelpActivity extends Activity { Button button = new Button(this); final String topic = name.substring(0, name.length() - SUFFIX.length()); button.setText(topic); - + button.setOnClickListener(new OnClickListener() { public void onClick(View v) { Intent intent = new Intent(HelpActivity.this, HelpTopicActivity.class); @@ -65,7 +65,7 @@ public class HelpActivity extends Activity { HelpActivity.this.startActivity(intent); } }); - + content.addView(button); } } diff --git a/src/org/connectbot/HelpTopicActivity.java b/src/org/connectbot/HelpTopicActivity.java index 35e23f5..6b54732 100644 --- a/src/org/connectbot/HelpTopicActivity.java +++ b/src/org/connectbot/HelpTopicActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -34,16 +34,16 @@ public class HelpTopicActivity extends Activity { public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.act_help_topic); - + String topic = getIntent().getStringExtra(Intent.EXTRA_TITLE); - + this.setTitle(String.format("%s: %s - %s", getResources().getText(R.string.app_name), getResources().getText(R.string.title_help), topic)); - + HelpTopicView helpTopic = (HelpTopicView) findViewById(R.id.topic_text); - - helpTopic.setTopic(topic); + + helpTopic.setTopic(topic); } } diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java index c0a6229..d5ed0db 100644 --- a/src/org/connectbot/HostEditorActivity.java +++ b/src/org/connectbot/HostEditorActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -47,15 +47,15 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr protected Map<String, String> values = new HashMap<String, String>(); // protected Map<String, String> pubkeys = new HashMap<String, String>(); - + public CursorPreferenceHack(String table, long id) { this.table = table; this.id = id; - + this.cacheValues(); - + } - + protected void cacheValues() { // fill a cursor and cache the values locally // this makes sure we dont have any floating cursor to dispose later @@ -64,22 +64,22 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr Cursor cursor = db.query(table, null, "_id = ?", new String[] { String.valueOf(id) }, null, null, null); cursor.moveToFirst(); - + for(int i = 0; i < cursor.getColumnCount(); i++) { String key = cursor.getColumnName(i); if(key.equals(HostDatabase.FIELD_HOST_HOSTKEY)) continue; String value = cursor.getString(i); values.put(key, value); } - + 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)); @@ -87,19 +87,19 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr // pubkeys.put(pubkeyid, value); // } while (cursor.moveToNext()); // } -// +// // cursor.close(); // db.close(); } - + public boolean contains(String key) { return values.containsKey(key); } - + public class Editor implements SharedPreferences.Editor { - + private ContentValues update = new ContentValues(); - + public SharedPreferences.Editor clear() { Log.d(this.getClass().toString(), "clear()"); update = new ContentValues(); @@ -111,15 +111,15 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr SQLiteDatabase db = hostdb.getWritableDatabase(); db.update(table, update, "_id = ?", new String[] { String.valueOf(id) }); db.close(); - + // make sure we refresh the parent cached values cacheValues(); - + // and update any listeners for(OnSharedPreferenceChangeListener listener : listeners) { listener.onSharedPreferenceChanged(CursorPreferenceHack.this, null); } - + return true; } @@ -150,7 +150,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr update.remove(key); return this; } - + } @@ -181,11 +181,11 @@ 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)); - + if(!values.containsKey(key)) return defValue; return values.get(key); } - + protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>(); public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { @@ -195,77 +195,77 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { listeners.remove(listener); } - + } - - + + @Override public SharedPreferences getSharedPreferences(String name, int mode) { //Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name)); return this.pref; } - + protected HostDatabase hostdb = null; private PubkeyDatabase pubkeydb = null; - + private CursorPreferenceHack pref; - + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - + long id = this.getIntent().getLongExtra(Intent.EXTRA_TITLE, -1); - + // TODO: we could pass through a specific ContentProvider uri here //this.getPreferenceManager().setSharedPreferencesName(uri); - + this.hostdb = new HostDatabase(this); this.pubkeydb = new PubkeyDatabase(this); - + this.pref = new CursorPreferenceHack(HostDatabase.TABLE_HOSTS, id); this.pref.registerOnSharedPreferenceChangeListener(this); - + this.addPreferencesFromResource(R.xml.host_prefs); - + // 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(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(pubkeydb.allValues("_id")); pubkeyPref.setEntryValues((CharSequence[]) pubkeyIds.toArray(new CharSequence[pubkeyIds.size()])); - this.updateSummaries(); + this.updateSummaries(); } - + public void onStart() { super.onStart(); if(this.hostdb == null) this.hostdb = new HostDatabase(this); - + if(this.pubkeydb == null) this.pubkeydb = new PubkeyDatabase(this); - + } - + public void onStop() { super.onStop(); if(this.hostdb != null) { this.hostdb.close(); this.hostdb = null; } - + if(this.pubkeydb != null) { this.pubkeydb.close(); this.pubkeydb = null; } } - + private void updateSummaries() { // for all text preferences, set hint as current database value for(String key : this.pref.values.keySet()) { @@ -274,7 +274,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr if(pref == null) continue; if(pref instanceof CheckBoxPreference) continue; String value = this.pref.getString(key, ""); - + if(key.equals("pubkeyid")) { try { int pubkeyId = Integer.parseInt(value); @@ -289,16 +289,16 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr // Fall through. } } - + pref.setSummary(value); } - + } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // update values on changed preference this.updateSummaries(); - + } } diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index 1c6a577..1988431 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -85,11 +85,11 @@ public class HostListActivity extends ListActivity { 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.updateList(); } - + public void onServiceDisconnected(ComponentName className) { bound = null; HostListActivity.this.updateList(); @@ -97,9 +97,9 @@ public class HostListActivity extends ListActivity { }; @Override - public void onStart() { + public void onStart() { super.onStart(); - + // start the terminal manager service this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); @@ -111,20 +111,20 @@ public class HostListActivity extends ListActivity { } @Override - public void onStop() { + public void onStop() { super.onStop(); this.unbindService(connection); - + if(this.hostdb != null) { this.hostdb.close(); this.hostdb = null; } - + } - - - - public final static String PREF_EULA = "eula", PREF_SORTBYCOLOR = "sortByColor"; + + + + public final static String PREF_EULA = "eula", PREF_SORTBYCOLOR = "sortByColor"; public final static int REQUEST_EDIT = 1; public final static int REQUEST_EULA = 2; @@ -146,18 +146,18 @@ public class HostListActivity extends ListActivity { this.finish(); } break; - + case REQUEST_EDIT: this.updateList(); break; - + } - + } - - + + protected boolean makingShortcut = false; - + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -166,24 +166,24 @@ public class HostListActivity extends ListActivity { this.setTitle(String.format("%s: %s", getResources().getText(R.string.app_name), getResources().getText(R.string.title_hosts_list))); - + // check for eula agreement this.prefs = PreferenceManager.getDefaultSharedPreferences(this); - + boolean agreed = prefs.getBoolean(PREF_EULA, false); if(!agreed) { this.startActivityForResult(new Intent(this, WizardActivity.class), REQUEST_EULA); } - + // 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 this.hostdb = new HostDatabase(this); ListView list = this.getListView(); - + this.sortedByColor = prefs.getBoolean(PREF_SORTBYCOLOR, false); this.updateList(); @@ -195,7 +195,7 @@ public class HostListActivity extends ListActivity { // launch off to console details 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", host.getUsername(), host.getHostname(), @@ -203,11 +203,11 @@ public class HostListActivity extends ListActivity { Intent contents = new Intent(Intent.ACTION_VIEW, uri); contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - + if (makingShortcut) { // create shortcut if requested ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(HostListActivity.this, R.drawable.icon); - + Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, contents); intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, host.getNickname()); @@ -215,11 +215,11 @@ public class HostListActivity extends ListActivity { setResult(RESULT_OK, intent); finish(); - + } else { // otherwise just launch activity to show this host HostListActivity.this.startActivity(contents); - } + } } }); @@ -231,10 +231,10 @@ public class HostListActivity extends ListActivity { text.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { - + if(event.getAction() == KeyEvent.ACTION_UP) return false; if(keyCode != KeyEvent.KEYCODE_ENTER) return false; - + // make sure we follow pattern if (text.getText().length() < 3) return false; @@ -244,13 +244,13 @@ public class HostListActivity extends ListActivity { text.setError(getString(R.string.list_format_error)); return false; } - + // create new host for entered string and then launch Uri uri = Uri.parse(String.format("ssh://%s", text.getText().toString())); String username = uri.getUserInfo(); String hostname = uri.getHost(); int port = uri.getPort(); - + String nickname; if(port == -1) { port = 22; @@ -258,12 +258,12 @@ public class HostListActivity extends ListActivity { } else { nickname = String.format("%s@%s:%d", username, hostname, port); } - + 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))); HostListActivity.this.startActivity(intent); @@ -280,19 +280,19 @@ public class HostListActivity extends ListActivity { this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - + @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - + // don't offer menus when creating shortcut if (makingShortcut) return true; - + sortcolor.setVisible(!sortedByColor); sortlast.setVisible(sortedByColor); - + return true; - } + } @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -300,7 +300,7 @@ public class HostListActivity extends ListActivity { // don't offer menus when creating shortcut if(makingShortcut) return true; - + // add host, ssh keys, about sortcolor = menu.add(R.string.list_menu_sortcolor); sortcolor.setIcon(android.R.drawable.ic_menu_share); @@ -311,7 +311,7 @@ public class HostListActivity extends ListActivity { return true; } }); - + sortlast = menu.add(R.string.list_menu_sortname); sortlast.setIcon(android.R.drawable.ic_menu_share); sortlast.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -325,7 +325,7 @@ public class HostListActivity extends ListActivity { MenuItem keys = menu.add(R.string.list_menu_pubkeys); keys.setIcon(android.R.drawable.ic_lock_lock); keys.setIntent(new Intent(HostListActivity.this, PubkeyListActivity.class)); - + MenuItem settings = menu.add(R.string.list_menu_settings); settings.setIcon(android.R.drawable.ic_menu_preferences); settings.setIntent(new Intent(HostListActivity.this, SettingsActivity.class)); @@ -333,11 +333,11 @@ public class HostListActivity extends ListActivity { MenuItem help = menu.add(R.string.list_menu_help); help.setIcon(android.R.drawable.ic_menu_help); help.setIntent(new Intent(HostListActivity.this, HelpActivity.class)); - + return true; - + } - + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { @@ -371,7 +371,7 @@ public class HostListActivity extends ListActivity { return true; } }); - + MenuItem portForwards = menu.add(R.string.list_host_portforwards); portForwards.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -389,32 +389,32 @@ public class HostListActivity extends ListActivity { new AlertDialog.Builder(HostListActivity.this) .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(true); + public void onClick(DialogInterface dialog, int which) { + // make sure we disconnect + if(bridge != null) + bridge.dispatchDisconnect(true); - hostdb.deleteHost(host); - updateHandler.sendEmptyMessage(-1); - } - }) - .setNegativeButton(R.string.delete_neg, null).create().show(); + hostdb.deleteHost(host); + updateHandler.sendEmptyMessage(-1); + } + }) + .setNegativeButton(R.string.delete_neg, null).create().show(); return true; } }); } - - protected void updateList() { + + 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) { @@ -424,28 +424,28 @@ public class HostListActivity extends ListActivity { } HostAdapter adapter = new HostAdapter(this, hosts, bound); - + this.setListAdapter(adapter); } - + class HostAdapter extends ArrayAdapter<HostBean> { private List<HostBean> 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<HostBean> 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. */ @@ -453,13 +453,13 @@ public class HostListActivity extends ListActivity { // 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; } @@ -475,14 +475,14 @@ public class HostListActivity extends ListActivity { if (host == null) { // Well, something bad happened. We can't continue. Log.e("HostAdapter", "Host bean is null!"); - + nickname.setText("Error during lookup"); caption.setText("see 'adb logcat' for more"); return view; } - + nickname.setText(host.getNickname()); - + switch (this.getConnectedState(host)) { case STATE_UNKNOWN: icon.setImageState(new int[] { }, true); @@ -494,7 +494,7 @@ public class HostListActivity extends ListActivity { icon.setImageState(new int[] { android.R.attr.state_expanded }, true); break; } - + ColorStateList chosen = null; if (HostDatabase.COLOR_RED.equals(host.getColor())) chosen = this.red; @@ -502,9 +502,9 @@ public class HostListActivity extends ListActivity { chosen = this.green; else if (HostDatabase.COLOR_BLUE.equals(host.getColor())) chosen = this.blue; - + if (chosen != null) { - // set color normally if not selected + // set color normally if not selected nickname.setTextColor(chosen); caption.setTextColor(chosen); } else { @@ -512,9 +512,9 @@ public class HostListActivity extends ListActivity { 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); @@ -530,7 +530,7 @@ public class HostListActivity extends ListActivity { } caption.setText(nice); - + return view; } } diff --git a/src/org/connectbot/PortForwardListActivity.java b/src/org/connectbot/PortForwardListActivity.java index 81cb2f3..656aa28 100644 --- a/src/org/connectbot/PortForwardListActivity.java +++ b/src/org/connectbot/PortForwardListActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -59,7 +59,7 @@ import android.widget.AdapterView.OnItemSelectedListener; /** * List all portForwards for a particular host and provide a way for users to add more portForwards, * edit existing portForwards, and delete portForwards. - * + * * @author Kenny Root */ public class PortForwardListActivity extends ListActivity { @@ -68,27 +68,27 @@ public class PortForwardListActivity extends ListActivity { protected HostDatabase hostdb; private List<PortForwardBean> portForwards; - + private ServiceConnection connection = null; protected TerminalBridge hostBridge = null; protected LayoutInflater inflater = null; - + private HostBean host; - + @Override - public void onStart() { + public void onStart() { super.onStart(); - - this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + + this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); if(this.hostdb == null) this.hostdb = new HostDatabase(this); } - + @Override - public void onStop() { + public void onStop() { super.onStop(); - + this.unbindService(connection); if(this.hostdb != null) { @@ -96,28 +96,28 @@ public class PortForwardListActivity extends ListActivity { this.hostdb = null; } } - + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - + 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); 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), + getResources().getText(R.string.title_port_forwards_list), 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.host.equals(host)) { hostBridge = bridge; @@ -126,44 +126,44 @@ public class PortForwardListActivity extends ListActivity { break; } } - - + + } public void onServiceDisconnected(ComponentName name) { hostBridge = null; } }; - + this.updateList(); - + this.registerForContextMenu(this.getListView()); - + this.getListView().setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> adapter, View view, int position, long id) { ListView lv = PortForwardListActivity.this.getListView(); PortForwardBean pfb = (PortForwardBean) lv.getItemAtPosition(position); - + if (hostBridge != null) { if (pfb.isEnabled()) hostBridge.disablePortForward(pfb); else { if (!hostBridge.enablePortForward(pfb)) - Toast.makeText(PortForwardListActivity.this, getString(R.string.portforward_problem), Toast.LENGTH_LONG).show(); + Toast.makeText(PortForwardListActivity.this, getString(R.string.portforward_problem), Toast.LENGTH_LONG).show(); } updateHandler.sendEmptyMessage(-1); } } }); - + this.inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - + MenuItem add = menu.add(R.string.portforward_menu_add); add.setIcon(android.R.drawable.ic_menu_add); add.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -172,16 +172,16 @@ public class PortForwardListActivity extends ListActivity { final View portForwardView = inflater.inflate(R.layout.dia_portforward, null, false); final EditText destEdit = (EditText) portForwardView.findViewById(R.id.portforward_destination); final Spinner typeSpinner = (Spinner)portForwardView.findViewById(R.id.portforward_type); - + typeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> value, View view, int position, long id) { destEdit.setEnabled(position != 2); } - public void onNothingSelected(AdapterView<?> arg0) { - } + public void onNothingSelected(AdapterView<?> arg0) { + } }); - + new AlertDialog.Builder(PortForwardListActivity.this) .setView(portForwardView) .setPositiveButton(R.string.portforward_pos, new DialogInterface.OnClickListener() { @@ -202,20 +202,20 @@ public class PortForwardListActivity extends ListActivity { type = HostDatabase.PORTFORWARD_DYNAMIC5; break; } - + PortForwardBean pfb = new PortForwardBean(host.getId(), nicknameEdit.getText().toString(), type, sourcePortEdit.getText().toString(), destEdit.getText().toString()); - + if (hostBridge != null) { hostBridge.addPortForward(pfb); hostBridge.enablePortForward(pfb); } - + if (!hostdb.savePortForward(pfb)) throw new Exception("Could not save port forward"); - + updateHandler.sendEmptyMessage(-1); } catch (Exception e) { Log.e(TAG, "Could not update port forward", e); @@ -224,14 +224,14 @@ public class PortForwardListActivity extends ListActivity { } }) .setNegativeButton(R.string.portforward_neg, null).create().show(); - + return true; } }); - + return true; } - + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { // Create menu to handle deleting and editing port forward @@ -239,12 +239,12 @@ public class PortForwardListActivity extends ListActivity { final PortForwardBean pfb = (PortForwardBean) this.getListView().getItemAtPosition(info.position); menu.setHeaderTitle(pfb.getNickname()); - + MenuItem edit = menu.add(R.string.portforward_edit); edit.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { final View editTunnelView = inflater.inflate(R.layout.dia_portforward, null, false); - + final Spinner typeSpinner = (Spinner) editTunnelView.findViewById(R.id.portforward_type); if (HostDatabase.PORTFORWARD_LOCAL.equals(pfb.getType())) typeSpinner.setSelection(0); @@ -258,30 +258,30 @@ public class PortForwardListActivity extends ListActivity { final EditText sourcePortEdit = (EditText) editTunnelView.findViewById(R.id.portforward_source); sourcePortEdit.setText(String.valueOf(pfb.getSourcePort())); - + final EditText destEdit = (EditText) editTunnelView.findViewById(R.id.portforward_destination); if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(pfb.getType())) { destEdit.setEnabled(false); } else { destEdit.setText(String.format("%s:%d", pfb.getDestAddr(), pfb.getDestPort())); } - + typeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> value, View view, int position, long id) { destEdit.setEnabled(position != 2); } - public void onNothingSelected(AdapterView<?> arg0) { - } + public void onNothingSelected(AdapterView<?> arg0) { + } }); - + new AlertDialog.Builder(PortForwardListActivity.this) .setView(editTunnelView) .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { try { pfb.setNickname(nicknameEdit.getText().toString()); - + switch (typeSpinner.getSelectedItemPosition()) { case 0: pfb.setType(HostDatabase.PORTFORWARD_LOCAL); @@ -293,19 +293,19 @@ public class PortForwardListActivity extends ListActivity { pfb.setType(HostDatabase.PORTFORWARD_DYNAMIC5); break; } - + pfb.setSourcePort(Integer.parseInt(sourcePortEdit.getText().toString())); pfb.setDest(destEdit.getText().toString()); - + // Use the new settings for the existing connection. if (hostBridge != null) { hostBridge.disablePortForward(pfb); hostBridge.enablePortForward(pfb); } - + if (!hostdb.savePortForward(pfb)) throw new Exception("Could not save port forward"); - + updateHandler.sendEmptyMessage(-1); } catch (Exception e) { Log.e(TAG, "Could not update port forward", e); @@ -314,11 +314,11 @@ public class PortForwardListActivity extends ListActivity { } }) .setNegativeButton(android.R.string.cancel, null).create().show(); - + return true; } }); - + MenuItem delete = menu.add(R.string.portforward_delete); delete.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -326,35 +326,35 @@ public class PortForwardListActivity extends ListActivity { new AlertDialog.Builder(PortForwardListActivity.this) .setMessage(getString(R.string.delete_message, pfb.getNickname())) .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - try { - // Delete the port forward from the host if needed. - if (hostBridge != null) - hostBridge.removePortForward(pfb); - - hostdb.deletePortForward(pfb); - } catch (Exception e) { - Log.e(TAG, "Could not delete port forward", e); - } - - updateHandler.sendEmptyMessage(-1); - } - }) - .setNegativeButton(R.string.delete_neg, null).create().show(); - + public void onClick(DialogInterface dialog, int which) { + try { + // Delete the port forward from the host if needed. + if (hostBridge != null) + hostBridge.removePortForward(pfb); + + hostdb.deletePortForward(pfb); + } catch (Exception e) { + Log.e(TAG, "Could not delete port forward", e); + } + + updateHandler.sendEmptyMessage(-1); + } + }) + .setNegativeButton(R.string.delete_neg, null).create().show(); + return true; } }); } - + protected Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { PortForwardListActivity.this.updateList(); } }; - - protected void updateList() { + + protected void updateList() { if (hostBridge != null) { this.portForwards = hostBridge.getPortForwards(); } else { @@ -363,16 +363,16 @@ public class PortForwardListActivity extends ListActivity { } PortForwardAdapter adapter = new PortForwardAdapter(this, portForwards); - + this.setListAdapter(adapter); } - + class PortForwardAdapter extends ArrayAdapter<PortForwardBean> { private List<PortForwardBean> portForwards; public PortForwardAdapter(Context context, List<PortForwardBean> portForwards) { super(context, R.layout.item_portforward, portForwards); - + this.portForwards = portForwards; } @@ -386,12 +386,12 @@ public class PortForwardListActivity extends ListActivity { PortForwardBean pfb = portForwards.get(position); nickname.setText(pfb.getNickname()); caption.setText(pfb.getDescription()); - + if (hostBridge != null && !pfb.isEnabled()) { nickname.setPaintFlags(nickname.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); caption.setPaintFlags(caption.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } - + return view; } } diff --git a/src/org/connectbot/PubkeyListActivity.java b/src/org/connectbot/PubkeyListActivity.java index aa02a16..8e89a89 100644 --- a/src/org/connectbot/PubkeyListActivity.java +++ b/src/org/connectbot/PubkeyListActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -74,7 +74,7 @@ import com.trilead.ssh2.crypto.PEMStructure; /** * List public keys in database by nickname and describe their properties. Allow users to import, * generate, rename, and delete key pairs. - * + * * @author Kenny Root */ public class PubkeyListActivity extends ListActivity implements EventListener { @@ -82,13 +82,13 @@ public class PubkeyListActivity extends ListActivity implements EventListener { protected PubkeyDatabase pubkeydb; private List<PubkeyBean> pubkeys; - + protected ClipboardManager clipboard; - + protected LayoutInflater inflater = null; - + protected TerminalManager bound = null; - + private MenuItem onstartToggle = null; private ServiceConnection connection = new ServiceConnection() { @@ -106,19 +106,19 @@ public class PubkeyListActivity extends ListActivity implements EventListener { }; @Override - public void onStart() { + public void onStart() { super.onStart(); - + bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); if(pubkeydb == null) pubkeydb = new PubkeyDatabase(this); } - + @Override - public void onStop() { + public void onStop() { super.onStop(); - + unbindService(connection); if(pubkeydb != null) { @@ -126,28 +126,28 @@ public class PubkeyListActivity extends ListActivity implements EventListener { pubkeydb = null; } } - + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.act_pubkeylist); - + this.setTitle(String.format("%s: %s", getResources().getText(R.string.app_name), getResources().getText(R.string.title_pubkey_list))); - + // connect with hosts database and populate list pubkeydb = new PubkeyDatabase(this); - + updateList(); - + registerForContextMenu(getListView()); - + getListView().setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> adapter, View view, int position, long id) { PubkeyBean pubkey = (PubkeyBean) getListView().getItemAtPosition(position); boolean loaded = bound.isKeyLoaded(pubkey.getNickname()); - + // handle toggling key in-memory on/off if(loaded) { bound.removeKey(pubkey.getNickname()); @@ -158,19 +158,19 @@ public class PubkeyListActivity extends ListActivity implements EventListener { } }); - + clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); - + inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - + /** * Read given file into memory as <code>byte[]</code>. */ protected 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) { @@ -182,13 +182,13 @@ public class PubkeyListActivity extends ListActivity implements EventListener { 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_menu_manage); generatekey.setIntent(new Intent(PubkeyListActivity.this, GeneratePubkeyActivity.class)); @@ -197,14 +197,14 @@ public class PubkeyListActivity extends ListActivity implements EventListener { 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()); - + // Don't show a dialog if the SD card is completely absent. final String state = Environment.getExternalStorageState(); if (Environment.MEDIA_REMOVED.equals(state) @@ -216,14 +216,14 @@ public class PubkeyListActivity extends ListActivity implements EventListener { .setNegativeButton(android.R.string.cancel, null).create().show(); return true; } - + List<String> names = new LinkedList<String>(); for(File file : sdcard.listFiles()) { if(file.isDirectory()) continue; names.add(file.getName()); } Collections.sort(names); - + final String[] namesList = names.toArray(new String[] {}); Log.d(TAG, names.toString()); @@ -238,24 +238,24 @@ public class PubkeyListActivity extends ListActivity implements EventListener { String name = namesList[arg1]; pubkey.setNickname(name); 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); - + String data = new String(raw); if (data.startsWith(PubkeyUtils.PKCS8_START)) { int start = data.indexOf(PubkeyUtils.PKCS8_START) + PubkeyUtils.PKCS8_START.length(); int end = data.indexOf(PubkeyUtils.PKCS8_END); - + if (end > start) { char[] encoded = data.substring(start, end - 1).toCharArray(); Log.d(TAG, "encoded: " + new String(encoded)); byte[] decoded = Base64.decode(encoded); - + KeyPair kp = PubkeyUtils.recoverKeyPair(decoded); - + pubkey.setType(kp.getPrivate().getAlgorithm()); pubkey.setPrivateKey(kp.getPrivate().getEncoded()); pubkey.setPublicKey(kp.getPublic().getEncoded()); @@ -271,7 +271,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED); pubkey.setPrivateKey(raw); } - + // write new value into database pubkeydb.savePubkey(pubkey); updateHandler.sendEmptyMessage(-1); @@ -286,28 +286,28 @@ public class PubkeyListActivity extends ListActivity implements EventListener { return true; } }); - + return true; } - - protected void handleAddKey(final PubkeyBean pubkey) { + + protected void handleAddKey(final PubkeyBean pubkey) { if (pubkey.isEncrypted()) { 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(R.string.pubkey_unlock, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - handleAddKey(pubkey, passwordField.getText().toString()); - } - }) - .setNegativeButton(android.R.string.cancel, null).create().show(); + public void onClick(DialogInterface dialog, int which) { + handleAddKey(pubkey, passwordField.getText().toString()); + } + }) + .setNegativeButton(android.R.string.cancel, null).create().show(); } else { handleAddKey(pubkey, null); } } - + protected void handleAddKey(PubkeyBean pubkey, String password) { Object trileadKey = null; if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType())) { @@ -319,7 +319,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { Log.e(TAG, message, e); Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG); } - + } else { // load using internal generated format PrivateKey privKey = null; @@ -340,17 +340,17 @@ public class PubkeyListActivity extends ListActivity implements EventListener { } if(trileadKey == null) return; - + Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname())); // save this key in-memory if option enabled if(bound.isSavingKeys()) { bound.addKey(pubkey.getNickname(), trileadKey); } - + updateHandler.sendEmptyMessage(-1); } - + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { // Create menu to handle deleting and editing pubkey @@ -358,10 +358,10 @@ public class PubkeyListActivity extends ListActivity implements EventListener { final PubkeyBean pubkey = (PubkeyBean) getListView().getItemAtPosition(info.position); menu.setHeaderTitle(pubkey.getNickname()); - + // TODO: option load/unload key from in-memory list // prompt for password as needed for passworded keys - + // cant change password or clipboard imported keys final boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType()); final boolean loaded = bound.isKeyLoaded(pubkey.getNickname()); @@ -379,7 +379,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { return true; } }); - + onstartToggle = menu.add(R.string.pubkey_load_on_start); onstartToggle.setEnabled(!pubkey.isEncrypted()); onstartToggle.setCheckable(true); @@ -409,21 +409,21 @@ public class PubkeyListActivity extends ListActivity implements EventListener { return true; } }); - - MenuItem copyPrivateToClipboard = menu.add(R.string.pubkey_copy_private); - copyPrivateToClipboard.setEnabled(!pubkey.isEncrypted() || imported); + + MenuItem copyPrivateToClipboard = menu.add(R.string.pubkey_copy_private); + copyPrivateToClipboard.setEnabled(!pubkey.isEncrypted() || imported); copyPrivateToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { try { String data = null; - + if (imported) data = new String(pubkey.getPrivateKey()); else { PrivateKey pk = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType()); data = PubkeyUtils.exportPEM(pk, null); } - + clipboard.setText(data); } catch (Exception e) { e.printStackTrace(); @@ -431,7 +431,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { return true; } }); - + MenuItem changePassword = menu.add(R.string.pubkey_change_password); changePassword.setEnabled(!imported); changePassword.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -446,7 +446,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { String oldPassword = ((EditText)changePasswordView.findViewById(R.id.old_password)).getText().toString(); String password1 = ((EditText)changePasswordView.findViewById(R.id.password1)).getText().toString(); String password2 = ((EditText)changePasswordView.findViewById(R.id.password2)).getText().toString(); - + if (!password1.equals(password2)) { new AlertDialog.Builder(PubkeyListActivity.this) .setMessage(R.string.alert_passwords_do_not_match_msg) @@ -454,7 +454,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { .create().show(); return; } - + try { if (!pubkey.changePassword(oldPassword, password1)) new AlertDialog.Builder(PubkeyListActivity.this) @@ -475,11 +475,11 @@ public class PubkeyListActivity extends ListActivity implements EventListener { } }) .setNegativeButton(android.R.string.cancel, null).create().show(); - + return true; } }); - + MenuItem delete = menu.add(R.string.pubkey_delete); delete.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -487,25 +487,25 @@ public class PubkeyListActivity extends ListActivity implements EventListener { new AlertDialog.Builder(PubkeyListActivity.this) .setMessage(getString(R.string.delete_message, pubkey.getNickname())) .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - // dont forget to remove from in-memory - if(loaded) - bound.removeKey(pubkey.getNickname()); - - // delete from backend database and update gui - pubkeydb.deletePubkey(pubkey); - updateHandler.sendEmptyMessage(-1); - } - }) - .setNegativeButton(R.string.delete_neg, null).create().show(); + public void onClick(DialogInterface dialog, int which) { + + // dont forget to remove from in-memory + if(loaded) + bound.removeKey(pubkey.getNickname()); + + // delete from backend database and update gui + pubkeydb.deletePubkey(pubkey); + updateHandler.sendEmptyMessage(-1); + } + }) + .setNegativeButton(R.string.delete_neg, null).create().show(); return true; } }); - + } - + protected Handler updateHandler = new Handler() { @Override @@ -513,22 +513,22 @@ public class PubkeyListActivity extends ListActivity implements EventListener { updateList(); } }; - + protected void updateList() { if (pubkeydb == null) return; - + pubkeys = pubkeydb.allPubkeys(); PubkeyAdapter adapter = new PubkeyAdapter(this, pubkeys); - + this.setListAdapter(adapter); } - + class PubkeyAdapter extends ArrayAdapter<PubkeyBean> { private List<PubkeyBean> pubkeys; public PubkeyAdapter(Context context, List<PubkeyBean> pubkeys) { super(context, R.layout.item_pubkey, pubkeys); - + this.pubkeys = pubkeys; } @@ -542,7 +542,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { PubkeyBean pubkey = pubkeys.get(position); nickname.setText(pubkey.getNickname()); - + boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType()); if (imported) { @@ -562,18 +562,18 @@ public class PubkeyListActivity extends ListActivity implements EventListener { caption.setText(R.string.pubkey_unknown_format); } } - + if (bound == null) { icon.setVisibility(View.GONE); } else { icon.setVisibility(View.VISIBLE); - + if (bound.isKeyLoaded(pubkey.getNickname())) icon.setImageState(new int[] { android.R.attr.state_checked }, true); else icon.setImageState(new int[] { }, true); } - + return view; } } diff --git a/src/org/connectbot/SettingsActivity.java b/src/org/connectbot/SettingsActivity.java index 8c31c5b..422e1df 100644 --- a/src/org/connectbot/SettingsActivity.java +++ b/src/org/connectbot/SettingsActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java index 3feebd5..067d54b 100644 --- a/src/org/connectbot/TerminalView.java +++ b/src/org/connectbot/TerminalView.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -34,7 +34,7 @@ import android.widget.Toast; * User interface {@link View} for showing a TerminalBridge in an * {@link Activity}. Handles drawing bitmap updates and passing keystrokes down * to terminal. - * + * * @author jsharkey */ public class TerminalView extends View { @@ -43,63 +43,63 @@ public class TerminalView extends View { public final TerminalBridge bridge; private final Paint paint; private final Paint cursorPaint; - + private Toast notification = null; private String lastNotification = null; public TerminalView(Context context, TerminalBridge bridge) { super(context); - + this.context = context; this.bridge = bridge; paint = new Paint(); - + setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); setFocusable(true); setFocusableInTouchMode(true); - + cursorPaint = new Paint(); cursorPaint.setColor(bridge.color[TerminalBridge.COLOR_FG_STD]); cursorPaint.setXfermode(new PixelXorXfermode(bridge.color[TerminalBridge.COLOR_BG_STD])); - + // connect our view up to the bridge setOnKeyListener(bridge); - - + + } - + public void destroy() { // tell bridge to destroy its bitmap bridge.parentDestroyed(); } - + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); bridge.parentChanged(this); } - + @Override public void onDraw(Canvas canvas) { if(bridge.bitmap != null) { // draw the bitmap bridge.onDraw(); - + // draw the bridge bitmap if it exists canvas.drawBitmap(bridge.bitmap, 0, 0, paint); - + // also draw cursor if visible if(bridge.buffer.isCursorVisible()) { int x = bridge.buffer.getCursorColumn() * bridge.charWidth; int y = (bridge.buffer.getCursorRow() + bridge.buffer.screenBase - bridge.buffer.windowBase) * bridge.charHeight; - + canvas.drawRect(x, y, x + bridge.charWidth, y + bridge.charHeight, cursorPaint); - + } - + // draw any highlighted area if (bridge.isSelectingForCopy()) { SelectionArea area = bridge.getSelectionArea(); @@ -113,20 +113,20 @@ public class TerminalView extends View { } } } - + public void notifyUser(String message) { if (notification != null) { // Don't keep telling the user the same thing. if (lastNotification != null && lastNotification.equals(message)) return; - + notification.setText(message); notification.show(); } else { notification = Toast.makeText(context, message, Toast.LENGTH_SHORT); notification.show(); } - + lastNotification = message; } diff --git a/src/org/connectbot/WizardActivity.java b/src/org/connectbot/WizardActivity.java index abfcdd5..9e76ba0 100644 --- a/src/org/connectbot/WizardActivity.java +++ b/src/org/connectbot/WizardActivity.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -32,20 +32,20 @@ import android.widget.ViewFlipper; /** * Show a series of wizard-like steps to the user, which might include an EULA, * program credits, and helpful hints. - * + * * @author jsharkey */ public class WizardActivity extends Activity { protected ViewFlipper flipper = null; private Button next, prev; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_wizard); - + this.flipper = (ViewFlipper) findViewById(R.id.wizard_flipper); - + // inflate the layout for EULA step LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.flipper.addView(inflater.inflate(R.layout.wiz_eula, this.flipper, false)); @@ -55,7 +55,7 @@ public class WizardActivity extends Activity { for (String topic : topics) { flipper.addView(new HelpTopicView(this).setTopic(topic)); } - + next = (Button)this.findViewById(R.id.action_next); next.setOnClickListener(new OnClickListener() { public void onClick(View v) { @@ -70,7 +70,7 @@ public class WizardActivity extends Activity { } } }); - + prev = (Button)this.findViewById(R.id.action_prev); prev.setOnClickListener(new OnClickListener() { public void onClick(View v) { @@ -85,21 +85,21 @@ public class WizardActivity extends Activity { } } }); - - this.updateButtons(); + + this.updateButtons(); } - + protected boolean isFirstDisplayed() { return (flipper.getDisplayedChild() == 0); } - + protected boolean isLastDisplayed() { return (flipper.getDisplayedChild() == flipper.getChildCount() - 1); } - + protected void updateButtons() { boolean eula = (flipper.getDisplayedChild() == 0); - + next.setText(eula ? getString(R.string.wizard_agree) : getString(R.string.wizard_next)); prev.setText(eula ? getString(R.string.wizard_cancel) : getString(R.string.wizard_back)); } diff --git a/src/org/connectbot/bean/AbstractBean.java b/src/org/connectbot/bean/AbstractBean.java index 4998727..da5991a 100644 --- a/src/org/connectbot/bean/AbstractBean.java +++ b/src/org/connectbot/bean/AbstractBean.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -30,10 +30,10 @@ import android.content.ContentValues; abstract class AbstractBean { public abstract ContentValues getValues(); public abstract String getBeanName(); - + public String toXML() { XmlBuilder xml = new XmlBuilder(); - + xml.append(String.format("<%s>", getBeanName())); ContentValues values = getValues(); @@ -43,7 +43,7 @@ abstract class AbstractBean { xml.append(entry.getKey(), value); } xml.append(String.format("</%s>", getBeanName())); - + return xml.toString(); } } diff --git a/src/org/connectbot/bean/HostBean.java b/src/org/connectbot/bean/HostBean.java index bd8f424..fbde26f 100644 --- a/src/org/connectbot/bean/HostBean.java +++ b/src/org/connectbot/bean/HostBean.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -27,7 +27,7 @@ import android.content.ContentValues; */ public class HostBean extends AbstractBean { public static final String BEAN_NAME = "host"; - + /* Database fields */ private long id = -1; private String nickname = null; @@ -46,20 +46,20 @@ public class HostBean extends AbstractBean { private String encoding = HostDatabase.ENCODING_ASCII; public HostBean() { - + } - + public String getBeanName() { return BEAN_NAME; } - + 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; } @@ -158,19 +158,19 @@ public class HostBean extends AbstractBean { public String getEncoding() { return this.encoding; } - + 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); @@ -185,36 +185,36 @@ public class HostBean extends AbstractBean { values.put(HostDatabase.FIELD_HOST_WANTSESSION, Boolean.toString(wantSession)); values.put(HostDatabase.FIELD_HOST_COMPRESSION, Boolean.toString(compression)); values.put(HostDatabase.FIELD_HOST_ENCODING, encoding); - + return values; } - + @Override 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; } @Override public int hashCode() { int hash = 7; - + hash = 31 * hash + (null == nickname ? 0 : nickname.hashCode()); hash = 31 * hash + (null == username ? 0 : username.hashCode()); hash = 31 * hash + (null == hostname ? 0 : hostname.hashCode()); hash = 31 * hash + port; - + return hash; } - + } diff --git a/src/org/connectbot/bean/PortForwardBean.java b/src/org/connectbot/bean/PortForwardBean.java index 9a2036f..d497d2c 100644 --- a/src/org/connectbot/bean/PortForwardBean.java +++ b/src/org/connectbot/bean/PortForwardBean.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -28,7 +28,7 @@ import android.content.ContentValues; */ public class PortForwardBean extends AbstractBean { public static final String BEAN_NAME = "portforward"; - + /* Database fields */ private long id = -1; private long hostId = -1; @@ -37,11 +37,11 @@ public class PortForwardBean extends AbstractBean { private int sourcePort = -1; private String destAddr = null; private int destPort = -1; - + /* Transient values */ private boolean enabled = false; private Object identifier = null; - + /** * @param id database ID of port forward * @param nickname Nickname to use to identify port forward @@ -59,7 +59,7 @@ public class PortForwardBean extends AbstractBean { this.destAddr = destAddr; this.destPort = destPort; } - + /** * @param type One of the port forward types from {@link HostDatabase} * @param source Source port number @@ -70,28 +70,28 @@ public class PortForwardBean extends AbstractBean { this.nickname = nickname; this.type = type; this.sourcePort = Integer.parseInt(source); - + this.setDest(dest); } public String getBeanName() { return BEAN_NAME; } - + /** * @param id the id to set */ public void setId(long id) { this.id = id; } - + /** * @return the id */ public long getId() { return id; } - + /** * @param nickname the nickname to set */ @@ -112,28 +112,28 @@ public class PortForwardBean extends AbstractBean { public void setType(String type) { this.type = type; } - + /** * @return the type */ public String getType() { return type; } - + /** * @param sourcePort the sourcePort to set */ public void setSourcePort(int sourcePort) { this.sourcePort = sourcePort; } - + /** * @return the sourcePort */ public int getSourcePort() { return sourcePort; } - + /** * @param dest The destination in "host:port" format */ @@ -143,42 +143,42 @@ public class PortForwardBean extends AbstractBean { if (destSplit.length > 1) this.destPort = Integer.parseInt(destSplit[1]); } - + /** * @param destAddr the destAddr to set */ public void setDestAddr(String destAddr) { this.destAddr = destAddr; } - + /** * @return the destAddr */ public String getDestAddr() { return destAddr; } - + /** * @param destPort the destPort to set */ public void setDestPort(int destPort) { this.destPort = destPort; } - + /** * @return the destPort */ public int getDestPort() { return destPort; } - + /** * @param enabled the enabled to set */ public void setEnabled(boolean enabled) { this.enabled = enabled; } - + /** * @return the enabled */ @@ -205,11 +205,11 @@ public class PortForwardBean extends AbstractBean { */ public CharSequence getDescription() { String description = "Unknown type"; - + if (HostDatabase.PORTFORWARD_LOCAL.equals(type)) { description = String.format("Local port %d to %s:%d", sourcePort, destAddr, destPort); } else if (HostDatabase.PORTFORWARD_REMOTE.equals(type)) { - description = String.format("Remote port %d to %s:%d", sourcePort, destAddr, destPort); + description = String.format("Remote port %d to %s:%d", sourcePort, destAddr, destPort); /* I don't think we need the SOCKS4 type. } else if (HostDatabase.PORTFORWARD_DYNAMIC4.equals(type)) { description = String.format("Dynamic port %d (SOCKS4)", sourcePort); @@ -217,7 +217,7 @@ public class PortForwardBean extends AbstractBean { } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(type)) { description = String.format("Dynamic port %d (SOCKS)", sourcePort); } - + return description; } @@ -226,14 +226,14 @@ public class PortForwardBean extends AbstractBean { */ public ContentValues getValues() { ContentValues values = new ContentValues(); - + values.put(HostDatabase.FIELD_PORTFORWARD_HOSTID, hostId); values.put(HostDatabase.FIELD_PORTFORWARD_NICKNAME, nickname); values.put(HostDatabase.FIELD_PORTFORWARD_TYPE, type); values.put(HostDatabase.FIELD_PORTFORWARD_SOURCEPORT, sourcePort); values.put(HostDatabase.FIELD_PORTFORWARD_DESTADDR, destAddr); values.put(HostDatabase.FIELD_PORTFORWARD_DESTPORT, destPort); - + return values; } } diff --git a/src/org/connectbot/bean/PubkeyBean.java b/src/org/connectbot/bean/PubkeyBean.java index c103a07..522369d 100644 --- a/src/org/connectbot/bean/PubkeyBean.java +++ b/src/org/connectbot/bean/PubkeyBean.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -30,7 +30,7 @@ import android.content.ContentValues; */ public class PubkeyBean extends AbstractBean { public static final String BEAN_NAME = "pubkey"; - + /* Database fields */ private long id; private String nickname; @@ -39,15 +39,15 @@ public class PubkeyBean extends AbstractBean { private byte[] publicKey; private boolean encrypted = false; private boolean startup = false; - + /* Transient values */ private boolean unlocked = false; private Object unlockedPrivate = null; - + public String getBeanName() { return BEAN_NAME; } - + public void setId(long id) { this.id = id; } @@ -138,29 +138,29 @@ public class PubkeyBean extends AbstractBean { @Override public ContentValues getValues() { ContentValues values = new ContentValues(); - + values.put(PubkeyDatabase.FIELD_PUBKEY_NICKNAME, nickname); values.put(PubkeyDatabase.FIELD_PUBKEY_TYPE, type); values.put(PubkeyDatabase.FIELD_PUBKEY_PRIVATE, privateKey); values.put(PubkeyDatabase.FIELD_PUBKEY_PUBLIC, publicKey); values.put(PubkeyDatabase.FIELD_PUBKEY_ENCRYPTED, encrypted ? 1 : 0); values.put(PubkeyDatabase.FIELD_PUBKEY_STARTUP, startup ? 1 : 0); - + return values; } public boolean changePassword(String oldPassword, String newPassword) throws Exception { PrivateKey priv; - + try { priv = PubkeyUtils.decodePrivate(getPrivateKey(), getType(), oldPassword); } catch (Exception e) { return false; } - + setPrivateKey(PubkeyUtils.getEncodedPrivate(priv, newPassword)); setEncrypted(newPassword.length() > 0); - + return true; } } diff --git a/src/org/connectbot/bean/SelectionArea.java b/src/org/connectbot/bean/SelectionArea.java index 6f13193..6cb7f4c 100644 --- a/src/org/connectbot/bean/SelectionArea.java +++ b/src/org/connectbot/bean/SelectionArea.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -33,16 +33,16 @@ public class SelectionArea { private int maxColumns; private int maxRows; private boolean selectingOrigin; - + public SelectionArea() { reset(); } - + public void reset() { top = left = bottom = right = 0; selectingOrigin = true; } - + /** * @param columns * @param rows @@ -51,7 +51,7 @@ public class SelectionArea { maxColumns = columns - 1; maxRows = rows - 1; } - + private int checkBounds(int value, int max) { if (value < 0) return 0; @@ -60,115 +60,115 @@ public class SelectionArea { else return value; } - + public boolean isSelectingOrigin() { return selectingOrigin; } - + public void finishSelectingOrigin() { selectingOrigin = false; } - + public void setTop(int top) { this.top = bottom = checkBounds(top, maxRows); } - + public void decrementTop() { setTop(--top); } - + public void incrementTop() { setTop(++top); } - + public int getTop() { return Math.min(top, bottom); } - + public void setBottom(int bottom) { this.bottom = checkBounds(bottom, maxRows); } - + public void decrementBottom() { setBottom(--bottom); } - + public void incrementBottom() { setBottom(++bottom); } - + public int getBottom() { return Math.max(top, bottom); } - + public void setLeft(int left) { this.left = right = checkBounds(left, maxColumns); } - + public void decrementLeft() { setLeft(--left); } - + public void incrementLeft() { setLeft(++left); } - + public int getLeft() { return Math.min(left, right); } - + public void setRight(int right) { this.right = checkBounds(right, maxColumns); } - + public void decrementRight() { setRight(--right); } - + public void incrementRight() { setRight(++right); } - + public int getRight() { return Math.max(left, right); } - + public String copyFrom(VDUBuffer vb) { int size = (getRight() - getLeft() + 1) * (getBottom() - getTop() + 1); - + StringBuffer buffer = new StringBuffer(size); - + for(int y = getTop(); y <= getBottom(); y++) { int lastNonSpace = buffer.length(); - + for (int x = getLeft(); x <= getRight(); x++) { // only copy printable chars char c = vb.getChar(x, y); - + if (!Character.isDefined(c) || (Character.isISOControl(c) && c != '\t')) c = ' '; - + if (c != ' ') lastNonSpace = buffer.length(); - + buffer.append(c); } - + // Don't leave a bunch of spaces in our copy buffer. if (buffer.length() > lastNonSpace) buffer.delete(lastNonSpace + 1, buffer.length()); - + if (y != bottom) buffer.append("\n"); } - + return buffer.toString(); } - + public String toString() { StringBuilder buffer = new StringBuilder(); - + buffer.append("SelectionArea[top="); buffer.append(top); buffer.append(", bottom="); @@ -184,7 +184,7 @@ public class SelectionArea { buffer.append(", isSelectingOrigin="); buffer.append(isSelectingOrigin()); buffer.append("]"); - + return buffer.toString(); } } diff --git a/src/org/connectbot/service/BridgeDisconnectedListener.java b/src/org/connectbot/service/BridgeDisconnectedListener.java index 74ab0f2..70c394e 100644 --- a/src/org/connectbot/service/BridgeDisconnectedListener.java +++ b/src/org/connectbot/service/BridgeDisconnectedListener.java @@ -1,17 +1,17 @@ /* 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/>. */ diff --git a/src/org/connectbot/service/PromptHelper.java b/src/org/connectbot/service/PromptHelper.java index 2fc5183..1523324 100644 --- a/src/org/connectbot/service/PromptHelper.java +++ b/src/org/connectbot/service/PromptHelper.java @@ -8,32 +8,32 @@ import android.os.Message; /** * Helps provide a relay for prompts and responses between a possible user * interface and some underlying service. - * + * * @author jsharkey */ public class PromptHelper { private final Object tag; private Handler handler = null; - + private Semaphore promptToken; private Semaphore promptResponse; - + public String promptHint = null; public Object promptRequested = null; - + private Object response = null; - + public PromptHelper(Object tag) { this.tag = tag; - + // Threads must acquire this before they can send a prompt. promptToken = new Semaphore(1); - + // Responses will release this semaphore. promptResponse = new Semaphore(0); } - + /** * Register a user interface handler, if available. @@ -50,7 +50,7 @@ public class PromptHelper { response = value; promptResponse.release(); } - + /** * Return the internal response value just before erasing and returning it. */ @@ -69,32 +69,32 @@ public class PromptHelper { */ private Object requestPrompt(String hint, Object type, boolean immediate) throws InterruptedException { Object response = null; - + if (immediate) cancelPrompt(); - + promptToken.acquire(); try { promptHint = hint; promptRequested = type; - + // notify any parent watching for live events if (handler != null) Message.obtain(handler, -1, tag).sendToTarget(); - + // acquire lock until user passes back value promptResponse.acquire(); promptRequested = null; - + response = popResponse(); } finally { promptToken.release(); } - + return response; } - + /** * Request a string response from parent. This is a blocking call until user * interface returns a value. @@ -110,7 +110,7 @@ public class PromptHelper { } return value; } - + /** * Convenience method for requestStringPrompt(String, boolean) * @param hint prompt hint for user to answer @@ -119,7 +119,7 @@ public class PromptHelper { public String requestStringPrompt(String hint) { return requestStringPrompt(hint, false); } - + /** * Request a boolean response from parent. This is a blocking call until user * interface returns a value. @@ -135,7 +135,7 @@ public class PromptHelper { } return value; } - + /** * Convenience method for requestBooleanPrompt(String, boolean) * @param hint String to present to user in prompt @@ -144,7 +144,7 @@ public class PromptHelper { public Boolean requestBooleanPrompt(String hint) { return requestBooleanPrompt(hint, false); } - + /** * Cancel an in-progress prompt. */ diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index 2522caa..5e7c2dc 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -82,53 +82,53 @@ import de.mud.terminal.vt320; * This separation allows us to keep the TerminalBridge running in a background * service. A TerminalView shares down a bitmap that we can use for rendering * when available. - * + * * This class also provides SSH hostkey verification prompting, and password * prompting. */ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCallback, ConnectionMonitor { - + public final static String TAG = TerminalBridge.class.toString(); - + private final static int BUFFER_SIZE = 4096; public final static int DEFAULT_FONT_SIZE = 10; - + public static final String AUTH_PUBLICKEY = "publickey", AUTH_PASSWORD = "password", AUTH_KEYBOARDINTERACTIVE = "keyboard-interactive"; - + protected final static int AUTH_TRIES = 20; private List<PortForwardBean> portForwards = new LinkedList<PortForwardBean>(); - + public int color[]; - + public final static int COLOR_FG_STD = 7; public final static int COLOR_BG_STD = 0; - + protected final TerminalManager manager; public HostBean host; - + public final Connection connection; protected Session session; - + private final Paint defaultPaint; protected OutputStream stdin; protected InputStream stdout; - + private InputStream stderr; private Thread relay; - + private final String emulation; private final int scrollback; public Bitmap bitmap = null; public VDUBuffer buffer = null; - + private TerminalView parent = null; private Canvas canvas = new Canvas(); @@ -139,7 +139,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private boolean tabKeyPressed = false; private boolean pubkeysExhausted = false; - + private boolean authenticated = false; private boolean sessionOpen = false; private boolean disconnected = false; @@ -148,9 +148,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private boolean forcedSize = false; private int termWidth; private int termHeight; - + private String keymode = null; - + private boolean selectingForCopy = false; private SelectionArea selectionArea; private ClipboardManager clipboard; @@ -162,9 +162,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private int charDescent = -1; private float fontSize = -1; - + private List<String> localOutput; - + /** * Flag indicating if we should perform a full-screen redraw during our next * rendering pass. @@ -178,17 +178,17 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal protected ConnectionInfo connectionInfo; public class HostKeyVerifier implements ServerHostKeyVerifier { - + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { // read in all known hosts from hostdb KnownHosts hosts = manager.hostdb.getKnownHosts(); Boolean result; - + String matchName = String.format("%s:%d", hostname, port); - + String fingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); - + String algorithmName; if ("ssh-rsa".equals(serverHostKeyAlgorithm)) algorithmName = "RSA"; @@ -196,7 +196,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal algorithmName = "DSA"; else algorithmName = serverHostKeyAlgorithm; - + switch(hosts.verifyHostkey(matchName, serverHostKeyAlgorithm, serverHostKey)) { case KnownHosts.HOSTKEY_IS_OK: outputLine(String.format("Verified host %s key: %s", algorithmName, fingerprint)); @@ -214,7 +214,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); } return result.booleanValue(); - + case KnownHosts.HOSTKEY_HAS_CHANGED: outputLine("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); outputLine("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); @@ -224,7 +224,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine("It is also possible that the host key has just been changed."); outputLine(String.format("Host %s key fingerprint is %s", algorithmName, fingerprint)); - + // Users have no way to delete keys, so we'll prompt them for now. result = promptHelper.requestBooleanPrompt("Are you sure you want\nto continue connecting?"); if(result == null) return false; @@ -232,41 +232,41 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // save this key in known database manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); } - return result.booleanValue(); + return result.booleanValue(); } - + return false; - + } - + } - + /** * Create new terminal bridge with following parameters. We will immediately * launch thread to start SSH connection and handle any hostkey verification * and password authentication. */ public TerminalBridge(final TerminalManager manager, final HostBean host) throws Exception { - + this.manager = manager; this.host = host; - + emulation = manager.getEmulation(); scrollback = manager.getScrollback(); // create prompt helper to relay password and hostkey requests up to gui promptHelper = new PromptHelper(this); - + // create our default paint defaultPaint = new Paint(); defaultPaint.setAntiAlias(true); defaultPaint.setTypeface(Typeface.MONOSPACE); defaultPaint.setFakeBoldText(true); // more readable? - + localOutput = new LinkedList<String>(); - + setFontSize(DEFAULT_FONT_SIZE); - + // create terminal buffer and handle outgoing data // this is probably status reply information buffer = new vt320() { @@ -291,12 +291,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal buffer.setBufferSize(scrollback); resetColors(); buffer.setDisplay(this); - + selectionArea = new SelectionArea(); // TODO Change this when hosts are beans as well portForwards = manager.hostdb.getPortForwardsForHost(host); - + // prepare the ssh connection for opening // we perform the actual connection later in startConnection() outputLine(String.format("Connecting to %s:%d", host.getHostname(), host.getPort())); @@ -304,7 +304,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal connection.addConnectionMonitor(this); connection.setCompression(host.getCompression()); } - + /** * Spawn thread to open connection and start login process. */ @@ -313,7 +313,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public void run() { try { connectionInfo = connection.connect(new HostKeyVerifier()); - + if (connectionInfo.clientToServerCryptoAlgorithm .equals(connectionInfo.serverToClientCryptoAlgorithm) && connectionInfo.clientToServerMACAlgorithm @@ -334,30 +334,30 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } } catch (IOException e) { Log.e(TAG, "Problem in SSH connection thread during authentication", e); - + // Display the reason in the text. outputLine(e.getCause().getMessage()); dispatchDisconnect(false); return; } - - try { + + try { // enter a loop to keep trying until authentication int tries = 0; while(!connection.isAuthenticationComplete() && tries++ < AUTH_TRIES && !disconnected) { handleAuthentication(); - + // sleep to make sure we dont kill system Thread.sleep(1000); } } catch(Exception e) { Log.e(TAG, "Problem in SSH connection thread during authentication", e); } - } + } }).start(); } - + /** * Attempt connection with database row pointed to by cursor. * @param cursor @@ -372,21 +372,21 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // load this key from memory if its already there Log.d(TAG, String.format("Found unlocked key '%s' already in-memory", pubkey.getNickname())); trileadKey = manager.getKey(pubkey.getNickname()); - + } else { // otherwise load key from database and prompt for password as needed String password = null; if (pubkey.isEncrypted()) { password = promptHelper.requestStringPrompt(String.format("Password for key '%s'", pubkey.getNickname())); - + // Something must have interrupted the prompt. if (password == null) return false; } - + if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType())) { // load specific key using pem format - trileadKey = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password); + trileadKey = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password); } else { // load using internal generated format PrivateKey privKey; @@ -399,10 +399,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine(message); return false; } - + PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType()); - + // convert key to trilead format trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey); Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey)); @@ -417,9 +417,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } return this.tryPublicKey(host.getUsername(), pubkey.getNickname(), trileadKey); - + } - + private 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); @@ -427,7 +427,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine(String.format("Authentication method 'publickey' with key '%s' failed", keyNickname)); return success; } - + protected void handleAuthentication() { try { if (connection.authenticateWithNone(host.getUsername())) { @@ -437,19 +437,19 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } catch(Exception e) { Log.d(TAG, "Host does not support 'none' authentication."); } - + outputLine("Trying to authenticate"); - + try { long pubkeyId = host.getPubkeyId(); - + if (!pubkeysExhausted && pubkeyId != HostDatabase.PUBKEYID_NEVER && 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 - + if (pubkeyId == HostDatabase.PUBKEYID_ANY) { // try each of the in-memory keys outputLine("Attempting 'publickey' authentication with any in-memory SSH keys"); @@ -464,14 +464,14 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal outputLine("Attempting 'publickey' authentication with a specific public key"); // use a specific key for this host, as requested PubkeyBean pubkey = manager.pubkeydb.findPubkeyById(pubkeyId); - + if (pubkey == null) outputLine("Selected public key is invalid, try reselecting key in host editor"); else if (tryPublicKey(pubkey)) finishConnection(); } - + pubkeysExhausted = true; } else if (connection.isAuthMethodAvailable(host.getUsername(), AUTH_PASSWORD)) { outputLine("Attempting 'password' authentication"); @@ -482,27 +482,27 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } else { outputLine("Authentication method 'password' failed"); } - + } else if(connection.isAuthMethodAvailable(host.getUsername(), AUTH_KEYBOARDINTERACTIVE)) { // this auth method will talk with us using InteractiveCallback interface - // it blocks until authentication finishes + // it blocks until authentication finishes outputLine("Attempting 'keyboard-interactive' authentication"); if(connection.authenticateWithKeyboardInteractive(host.getUsername(), TerminalBridge.this)) { finishConnection(); } else { outputLine("Authentication method 'keyboard-interactive' failed"); } - + } else { outputLine("[Your host doesn't support 'password' or 'keyboard-interactive' authentication.]"); - - } + + } } catch(Exception e) { Log.e(TAG, "Problem during handleAuthentication()", e); } - + } - + /** * Handle challenges from keyboard-interactive authentication mode. */ @@ -515,7 +515,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal return responses; } - + /** * Convenience method for writing a line into the underlying MUD buffer. * Should never be called once the session is established. @@ -523,12 +523,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal protected void outputLine(String line) { if (session != null) Log.e(TAG, "Session established, cannot use outputLine!", new IOException("outputLine call traceback")); - + synchronized (localOutput) { final String s = line + "\r\n"; - + localOutput.add(s); - + ((vt320) buffer).putString(s); } } @@ -549,14 +549,14 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } }).start(); } - + /** * Internal method to request actual PTY terminal once we've finished * authentication. If called before authenticated, it will just fail. */ private void finishConnection() { setAuthenticated(true); - + // Start up predefined port forwards for (PortForwardBean pfb : portForwards) { try { @@ -566,16 +566,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Error setting up port forward during connect", e); } } - + if (!host.getWantSession()) { outputLine("Session will not be started due to host preference."); return; } - + try { session = connection.openSession(); ((vt320) buffer).reset(); - + // We no longer need our local output. localOutput.clear(); @@ -585,7 +585,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal ((vt320) buffer).setAnswerBack(emulation); session.requestPTY(emulation, termWidth, termHeight, 0, 0, null); session.startShell(); - + // grab stdin/out from newly formed session stdin = session.getStdin(); stdout = session.getStdout(); @@ -595,74 +595,74 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal relay = new Thread(new Runnable() { public void run() { final String encoding = host.getEncoding(); - + byte[] b = new byte[BUFFER_SIZE]; - + Charset charset = Charset.forName(encoding); - + /* Set up character set decoder to report any byte sequences * which are malformed so we can try to resume decoding it * on the next packet received. - * + * * UTF-8 byte sequences have a tendency to get truncated at * times. */ CharsetDecoder cd = charset.newDecoder(); cd.onUnmappableCharacter(CodingErrorAction.REPLACE); cd.onMalformedInput(CodingErrorAction.REPORT); - + CharsetDecoder replacer = charset.newDecoder(); replacer.onUnmappableCharacter(CodingErrorAction.REPLACE); replacer.onMalformedInput(CodingErrorAction.REPLACE); - + ByteBuffer bb; CharBuffer cb = CharBuffer.allocate(BUFFER_SIZE); - + int n = 0; int offset = 0; - + int conditions = ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.CLOSED | ChannelCondition.EOF; int newConditions = 0; - + while((newConditions & ChannelCondition.CLOSED) == 0) { try { newConditions = session.waitForCondition(conditions, 0); if ((newConditions & ChannelCondition.STDOUT_DATA) != 0) { while (stdout.available() > 0) { n = offset + stdout.read(b, offset, BUFFER_SIZE - offset); - + bb = ByteBuffer.wrap(b, 0, n); - CoderResult cr = cd.decode(bb, cb, true); - + CoderResult cr = cd.decode(bb, cb, true); + if (cr.isMalformed()) { int curpos = bb.position() - cr.length(); - + if (curpos > 0) { /* There is good data before the malformed section, so * pass this on immediately. */ ((vt320)buffer).putString(cb.array(), 0, cb.position()); } - + while (bb.position() < n) { bb = ByteBuffer.wrap(b, curpos, cr.length()); - + cb.clear(); replacer.decode(bb, cb, true); - + ((vt320) buffer).putString(cb.array(), 0, cb.position()); - + curpos += cr.length(); - + bb = ByteBuffer.wrap(b, curpos, n - curpos); - + cb.clear(); cr = cd.decode(bb, cb, true); } - + if (cr.isMalformed()) { /* If we still have malformed input, save the bytes for the next * read and try to parse it again. @@ -689,12 +689,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal ((vt320)buffer).putString(cb.array(), 0, cb.position()); offset = 0; } - + cb.clear(); } redraw(); } - + if ((newConditions & ChannelCondition.STDERR_DATA) != 0) { while (stderr.available() > 0) { n = stderr.read(b); @@ -705,7 +705,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal cb.clear(); } } - + if ((newConditions & ChannelCondition.EOF) != 0) { // The other side closed our channel, so let's disconnect. // TODO review whether any tunnel is in use currently. @@ -723,16 +723,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // force font-size to make sure we resizePTY as needed setFontSize(fontSize); - + sessionOpen = true; - + // finally send any post-login string, if requested injectString(host.getPostLogin()); } catch (IOException e1) { Log.e(TAG, "Problem while trying to create PTY in finishConnection()", e1); } - + } /** @@ -745,7 +745,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public void setOnDisconnectedListener(BridgeDisconnectedListener disconnectListener) { this.disconnectListener = disconnectListener; } - + /** * Force disconnection of this terminal bridge. */ @@ -753,7 +753,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // We don't need to do this multiple times. if (disconnected && !immediate) return; - + // 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() { @@ -763,11 +763,11 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal connection.close(); } }).start(); - + disconnected = true; authenticated = false; sessionOpen = false; - + if (immediate) { awaitingClose = true; if (disconnectListener != null) @@ -778,7 +778,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Boolean result = promptHelper.requestBooleanPrompt("Host has disconnected.\nClose session?", true); if (result == null || result.booleanValue()) { awaitingClose = true; - + // Tell the TerminalManager that we can be destroyed now. if (disconnectListener != null) disconnectListener.onDisconnected(TerminalBridge.this); @@ -787,7 +787,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal }).start(); } } - + public void refreshKeymode() { keymode = manager.getKeyMode(); } @@ -796,7 +796,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public Vibrator vibrator = null; public static final long VIBRATE_DURATION = 30; - + /** * Handle onKey() events coming down from a {@link TerminalView} above us. * We might collect these for our internal buffer when working with hostkeys @@ -810,7 +810,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // skip keys if we aren't connected yet or have been disconnected if (disconnected || session == null) return false; - + if ("Use right-side keys".equals(keymode)) { if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT && slashKeyPressed) { stdin.write('/'); @@ -828,7 +828,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal return true; } } - + return false; } @@ -843,48 +843,48 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal setFontSize(fontSize - 2); return true; } - + // skip keys if we aren't connected yet or have been disconnected if (disconnected || session == null) return false; - + // if we're in scrollback, scroll to bottom of window on input - if (buffer.windowBase != buffer.screenBase) + if (buffer.windowBase != buffer.screenBase) buffer.setWindowBase(buffer.screenBase); - + boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE); - + // otherwise pass through to existing session // print normal keys if (printing) { int metaState = event.getMetaState(); - + slashKeyPressed = tabKeyPressed = false; - + if (shiftPressed) { metaState |= KeyEvent.META_SHIFT_ON; shiftPressed = false; } - + if (altPressed) { metaState |= KeyEvent.META_ALT_ON; altPressed = false; } - + int key = keymap.get(keyCode, metaState); - + if (ctrlPressed) { - // Support CTRL-a through CTRL-z - if (key >= 0x61 && key <= 0x7A) - key -= 0x60; - // Support CTRL-A through CTRL-_ - else if (key >= 0x41 && key <= 0x5F) - key -= 0x40; - else if (key == 0x20) - key = 0x00; - ctrlPressed = false; + // Support CTRL-a through CTRL-z + if (key >= 0x61 && key <= 0x7A) + key -= 0x60; + // Support CTRL-A through CTRL-_ + else if (key >= 0x41 && key <= 0x5F) + key -= 0x40; + else if (key == 0x20) + key = 0x00; + ctrlPressed = false; } - + // handle pressing f-keys if((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { //Log.d(TAG, "yay pressing an fkey"); @@ -901,11 +901,11 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal case ')': ((vt320)buffer).keyPressed(vt320.KEY_F10, ' ', 0); return true; } } - + stdin.write(key); return true; } - + // try handling keymode shortcuts if("Use right-side keys".equals(keymode)) { switch(keyCode) { @@ -926,25 +926,25 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // look for special chars switch(keyCode) { case KeyEvent.KEYCODE_CAMERA: - + // 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)) { stdin.write(0x01); stdin.write(' '); - + } else if(manager.res.getString(R.string.list_camera_ctrla).equals(camera)) { 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; - + case KeyEvent.KEYCODE_DEL: stdin.write(0x08); return true; case KeyEvent.KEYCODE_ENTER: ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', event.getMetaState()); return true; case KeyEvent.KEYCODE_DPAD_LEFT: @@ -1007,16 +1007,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if (parent != null && clipboard != null) { // copy selected area to clipboard String copiedText = selectionArea.copyFrom(buffer); - + clipboard.setText(copiedText); parent.notifyUser(parent.getContext().getString(R.string.console_copy_done, copiedText.length())); - + selectingForCopy = false; selectionArea.reset(); } } - + redraw(); } else { // TODO: Add some visual indication of Ctrl state @@ -1028,7 +1028,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } return true; } - + } catch (IOException e) { Log.e(TAG, "Problem while trying to handle an onKey() event", e); try { @@ -1042,22 +1042,22 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.d(TAG, "Input before connection established ignored."); return true; } - + return false; } - + public void setSelectingForCopy(boolean selectingForCopy) { this.selectingForCopy = selectingForCopy; } - + public boolean isSelectingForCopy() { return selectingForCopy; } - + public SelectionArea getSelectionArea() { return selectionArea; } - + public synchronized void tryKeyVibrate() { if (bumpyArrows && vibrator != null) vibrator.vibrate(VIBRATE_DURATION); @@ -1070,24 +1070,24 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal private void setFontSize(float size) { if (size <= 0.0) return; - + defaultPaint.setTextSize(size); fontSize = size; - + // read new metrics to get exact pixel dimensions FontMetricsInt fm = defaultPaint.getFontMetricsInt(); charDescent = fm.descent; - + float[] widths = new float[1]; defaultPaint.getTextWidths("X", widths); charWidth = (int)widths[0]; charHeight = Math.abs(fm.top) + Math.abs(fm.descent) + 1; - + // refresh any bitmap with new font size if(parent != null) parentChanged(parent); } - + /** * Something changed in our parent {@link TerminalView}, maybe it's a new * parent, or maybe it's an updated font size. We should recalculate @@ -1097,37 +1097,37 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.parent = parent; int width = parent.getWidth(); int height = parent.getHeight(); - + bumpyArrows = manager.prefs.getBoolean(manager.res.getString(R.string.pref_bumpyarrows), true); vibrator = (Vibrator) parent.getContext().getSystemService(Context.VIBRATOR_SERVICE); clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE); - + if (!forcedSize) { // recalculate buffer size int newTermWidth, newTermHeight; newTermWidth = width / charWidth; newTermHeight = height / charHeight; - + // If nothing has changed in the terminal dimensions and not an intial // draw then don't blow away scroll regions and such. if (newTermWidth == termWidth && newTermHeight == termHeight) return; - + termWidth = newTermWidth; termHeight = newTermHeight; } - + // reallocate new bitmap if needed boolean newBitmap = (bitmap == null); if(bitmap != null) newBitmap = (bitmap.getWidth() != width || bitmap.getHeight() != height); - + if(newBitmap) { bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); canvas.setBitmap(bitmap); } - + // clear out any old buffer information defaultPaint.setColor(Color.BLACK); canvas.drawRect(0, 0, width, height, defaultPaint); @@ -1136,7 +1136,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if (forcedSize) { int borderX = (termWidth * charWidth) + 1; int borderY = (termHeight * charHeight) + 1; - + defaultPaint.setColor(Color.GRAY); defaultPaint.setStrokeWidth(0.0f); if (width >= borderX) @@ -1144,40 +1144,40 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if (height >= borderY) canvas.drawLine(0, borderY, borderX + 1, borderY, defaultPaint); } - + try { // request a terminal pty resize int prevRow = buffer.getCursorRow(); buffer.setScreenSize(termWidth, termHeight, true); - + // Work around weird vt320.java behavior where cursor is an offset from the bottom?? buffer.setCursorPosition(buffer.getCursorColumn(), prevRow); - + if(session != null) session.resizePTY(termWidth, termHeight); } catch(Exception e) { Log.e(TAG, "Problem while trying to resize screen or PTY", e); } - + // redraw local output if we don't have a sesson to receive our resize request if (session == null) { synchronized (localOutput) { ((vt320) buffer).reset(); - + for (String line : localOutput) ((vt320) buffer).putString(line); } } - + // force full redraw with new buffer size fullRedraw = true; redraw(); this.parent.notifyUser(String.format("%d x %d", termWidth, termHeight)); - + Log.i(TAG, String.format("parentChanged() now width=%d, height=%d", termWidth, termHeight)); } - + /** * Somehow our parent {@link TerminalView} was destroyed. Now we don't need * to redraw anywhere, and we can recycle our internal bitmap. @@ -1197,21 +1197,21 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public VDUBuffer getVDUBuffer() { return buffer; } - + public void onDraw() { int fg, bg; boolean entireDirty = buffer.update[0] || fullRedraw; - + // walk through all lines in the buffer for(int l = 0; l < buffer.height; l++) { - + // check if this line is dirty and needs to be repainted // also check for entire-buffer dirty flags if (!entireDirty && !buffer.update[l + 1]) continue; - + // reset dirty flag for this line buffer.update[l + 1] = false; - + // walk through all characters in this line for (int c = 0; c < buffer.width; c++) { int addr = 0; @@ -1220,7 +1220,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // reset default colors fg = color[COLOR_FG_STD]; bg = color[COLOR_BG_STD]; - + // check if foreground color attribute is set if ((currAttr & VDUBuffer.COLOR_FG) != 0) { int fgcolor = ((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1; @@ -1233,38 +1233,38 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // check if background color attribute is set if ((currAttr & VDUBuffer.COLOR_BG) != 0) bg = color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1]; - + // support character inversion by swapping background and foreground color if ((currAttr & VDUBuffer.INVERT) != 0) { int swapc = bg; bg = fg; fg = swapc; } - + // set underlined attributes if requested defaultPaint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0); - + // determine the amount of continuous characters with the same settings and print them all at once while(c + addr < buffer.width && buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr) { addr++; } - + // clear this dirty area with background color defaultPaint.setColor(bg); canvas.drawRect(c * charWidth, (l * charHeight) - 1, (c + addr) * charWidth, (l + 1) * charHeight, defaultPaint); - + // write the text string starting at 'c' for 'addr' number of characters defaultPaint.setColor(fg); if((currAttr & VDUBuffer.INVISIBLE) == 0) canvas.drawText(buffer.charArray[buffer.windowBase + l], c, addr, c * charWidth, ((l + 1) * charHeight) - charDescent - 2, defaultPaint); - + // advance to the next text block with different characteristics c += addr - 1; } } - + // reset entire-buffer flags buffer.update[0] = false; fullRedraw = false; @@ -1274,7 +1274,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if (parent != null) parent.postInvalidate(); } - + public void updateScrollBar() { } @@ -1295,20 +1295,20 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal float size = 8.0f; float step = 8.0f; float limit = 0.125f; - + int direction; while ((direction = fontSizeCompare(size, cols, rows, width, height)) < 0) size += step; - + if (direction == 0) { Log.d("fontsize", String.format("Found match at %f", size)); return; } - + step /= 2.0f; size -= step; - + while ((direction = fontSizeCompare(size, cols, rows, width, height)) != 0 && step >= limit) { step /= 2.0f; @@ -1318,35 +1318,35 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal size += step; } } - + if (direction > 0) size -= step; - + forcedSize = true; termWidth = cols; termHeight = rows; setFontSize(size); } - + private int fontSizeCompare(float size, int cols, int rows, int width, int height) { // read new metrics to get exact pixel dimensions defaultPaint.setTextSize(size); FontMetricsInt fm = defaultPaint.getFontMetricsInt(); - + float[] widths = new float[1]; defaultPaint.getTextWidths("X", widths); int termWidth = (int)widths[0] * cols; int termHeight = (Math.abs(fm.top) + Math.abs(fm.descent) + 1) * rows; - + Log.d("fontsize", String.format("font size %f resulted in %d x %d", size, termWidth, termHeight)); - + // Check to see if it fits in resolution specified. if (termWidth > width || termHeight > height) return 1; - + if (termWidth == width || termHeight == height) return 0; - + return -1; } @@ -1367,10 +1367,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public boolean removePortForward(PortForwardBean portForward) { // Make sure we don't have a phantom forwarder. disablePortForward(portForward); - + return portForwards.remove(portForward); } - + /** * @return the list of port forwards */ @@ -1389,7 +1389,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Attempt to enable port forward not in list"); return false; } - + if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { LocalPortForwarder lpf = null; try { @@ -1398,12 +1398,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Could not create local port forward", e); return false; } - + if (lpf == null) { Log.e(TAG, "returned LocalPortForwarder object is null"); return false; } - + portForward.setIdentifier(lpf); portForward.setEnabled(true); return true; @@ -1414,19 +1414,19 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Could not create remote port forward", e); return false; } - + portForward.setEnabled(false); return true; } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { DynamicPortForwarder dpf = null; - + try { dpf = connection.createDynamicPortForwarder(portForward.getSourcePort()); } catch (IOException e) { Log.e(TAG, "Could not create dynamic port forward", e); return false; } - + portForward.setIdentifier(dpf); portForward.setEnabled(true); return true; @@ -1448,16 +1448,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Attempt to disable port forward not in list"); return false; } - + if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { LocalPortForwarder lpf = null; lpf = (LocalPortForwarder)portForward.getIdentifier(); - + if (!portForward.isEnabled() || lpf == null) { Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); return false; } - + portForward.setEnabled(false); try { @@ -1466,7 +1466,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Could not stop local port forwarder, setting enabled to false", e); return false; } - + return true; } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { portForward.setEnabled(false); @@ -1477,26 +1477,26 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); return false; } - + return true; } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { DynamicPortForwarder dpf = null; dpf = (DynamicPortForwarder)portForward.getIdentifier(); - + if (!portForward.isEnabled() || dpf == null) { Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); return false; } - + portForward.setEnabled(false); - + try { dpf.close(); } catch (IOException e) { Log.e(TAG, "Could not stop dynamic port forwarder, setting enabled to false", e); return false; } - + return true; } else { // Unsupported type @@ -1525,7 +1525,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public boolean isAwaitingClose() { return awaitingClose; } - + /** * @return whether this connection had started and subsequently disconnected */ @@ -1541,7 +1541,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if (index < color.length && index >= 16) color[index] = 0xff000000 | red << 16 | green << 8 | blue; } - + public void resetColors() { color = new int[] { 0xff000000, // black diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index 2ab3d78..58a2280 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -53,38 +53,38 @@ import android.util.Log; * Manager for SSH connections that runs as a background service. This service * holds a list of currently connected SSH bridges that are ready for connection * up to a GUI if needed. - * + * * @author jsharkey */ public class TerminalManager extends Service implements BridgeDisconnectedListener { - + public final static String TAG = TerminalManager.class.toString(); - + public List<TerminalBridge> bridges = new LinkedList<TerminalBridge>(); public TerminalBridge defaultBridge = null; - + public List<HostBean> disconnected = new LinkedList<HostBean>(); - + public Handler disconnectHandler = null; protected HashMap<String, Object> loadedPubkeys = new HashMap<String, Object>(); - + protected Resources res; - + protected HostDatabase hostdb; protected PubkeyDatabase pubkeydb; protected SharedPreferences prefs; private String pref_emulation, pref_scrollback, pref_keymode, pref_memkeys, pref_wifilock; - + private final IBinder binder = new TerminalBinder(); private ConnectivityManager connectivityManager; private WifiManager.WifiLock wifilock; - + private Timer idleTimer; private final long IDLE_TIMEOUT = 300000; // 5 minutes - + @Override public void onCreate() { Log.i(TAG, "Starting background service"); @@ -94,30 +94,30 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen pref_keymode = getResources().getString(R.string.pref_keymode); pref_memkeys = getResources().getString(R.string.pref_memkeys); pref_wifilock = getResources().getString(R.string.pref_wifilock); - + res = getResources(); - + hostdb = new HostDatabase(this); pubkeydb = new PubkeyDatabase(this); - + // load all marked pubkeys into memory List<PubkeyBean> pubkeys = pubkeydb.getAllStartPubkeys(); - + for (PubkeyBean pubkey : pubkeys) { try { PrivateKey privKey = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType()); PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType()); Object trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey); - + loadedPubkeys.put(pubkey.getNickname(), trileadKey); Log.d(TAG, String.format("Added key '%s' to in-memory cache", pubkey.getNickname())); } catch (Exception e) { Log.d(TAG, String.format("Problem adding key '%s' to in-memory cache", pubkey.getNickname()), e); } } - + connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - + WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE); wifilock = manager.createWifiLock(TAG); } @@ -129,26 +129,26 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen // disconnect and dispose of any existing bridges for(TerminalBridge bridge : bridges) bridge.dispatchDisconnect(true); - + if(hostdb != null) { hostdb.close(); hostdb = null; } - + if(pubkeydb != null) { pubkeydb.close(); pubkeydb = null; } - + synchronized (this) { if (idleTimer != null) idleTimer.cancel(); } - + if (wifilock != null && wifilock.isHeld()) wifilock.release(); } - + /** * Open a new SSH session using the given parameters. */ @@ -157,27 +157,27 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen if (findBridge(host) != null) { throw new Exception("Connection already open for that nickname"); } - + TerminalBridge bridge = new TerminalBridge(this, host); bridge.setOnDisconnectedListener(this); bridge.startConnection(); bridges.add(bridge); - + // Add a reference to the WifiLock NetworkInfo info = connectivityManager.getActiveNetworkInfo(); if (isLockingWifi() && info.getType() == ConnectivityManager.TYPE_WIFI) { Log.d(TAG, "Acquiring WifiLock"); wifilock.acquire(); } - + // also update database with new connected time touchHost(host); } - + public String getEmulation() { return prefs.getString(pref_emulation, "screen"); } - + public int getScrollback() { int scrollback = 140; try { @@ -186,7 +186,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } return scrollback; } - + public boolean isSavingKeys() { return prefs.getBoolean(pref_memkeys, true); } @@ -194,11 +194,11 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public String getKeyMode() { return prefs.getString(pref_keymode, getString(R.string.list_keymode_right)); // "Use right-side keys" } - + public boolean isLockingWifi() { return prefs.getBoolean(pref_wifilock, true); } - + /** * Open a new SSH session by reading parameters from the given URI. Follows * format <code>ssh://user@host:port/#nickname</code> @@ -208,18 +208,18 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen String username = uri.getUserInfo(); String hostname = uri.getHost(); int port = uri.getPort(); - + 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}. @@ -239,7 +239,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } return null; } - + /** * Called by child bridge when somehow it's been disconnected. */ @@ -259,20 +259,20 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen Message.obtain(disconnectHandler, -1, bridge).sendToTarget(); } - + public boolean isKeyLoaded(String nickname) { return loadedPubkeys.containsKey(nickname); } - + public void addKey(String nickname, Object trileadKey) { loadedPubkeys.remove(nickname); loadedPubkeys.put(nickname, trileadKey); } - + public void removeKey(String nickname) { loadedPubkeys.remove(nickname); } - + public Object getKey(String nickname) { return loadedPubkeys.get(nickname); } @@ -280,12 +280,12 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen private void stopWithDelay() { // TODO add in a way to check whether keys loaded are encrypted and only // set timer when we have an encrypted key loaded - + if (loadedPubkeys.size() > 0) { synchronized (this) { if (idleTimer == null) idleTimer = new Timer(true); - + idleTimer.schedule(new IdleTask(), IDLE_TIMEOUT); } } else { @@ -293,7 +293,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen stopSelf(); } } - + protected void stopNow() { if (bridges.size() == 0) stopSelf(); @@ -305,41 +305,41 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen idleTimer = null; } } - + public class TerminalBinder extends Binder { public TerminalManager getService() { return TerminalManager.this; } } - + @Override public IBinder onBind(Intent intent) { Log.i(TAG, "Someone bound to TerminalManager"); - + stopIdleTimer(); - + // Make sure we stay running to maintain the bridges startService(new Intent(this, TerminalManager.class)); - + return binder; } - + @Override public void onRebind(Intent intent) { super.onRebind(intent); Log.i(TAG, "Someone rebound to TerminalManager"); - + stopIdleTimer(); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "Someone unbound from TerminalManager"); - + if (bridges.size() == 0) stopWithDelay(); - + return true; } diff --git a/src/org/connectbot/util/Encryptor.java b/src/org/connectbot/util/Encryptor.java index ebcfc12..f5f6c86 100644 --- a/src/org/connectbot/util/Encryptor.java +++ b/src/org/connectbot/util/Encryptor.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -19,7 +19,7 @@ package org.connectbot.util; /** * This class is from: - * + * * Encryptor.java * Copyright 2008 Zach Scrivena * zachscrivena@gmail.com @@ -39,166 +39,166 @@ import javax.crypto.spec.SecretKeySpec; */ public final class Encryptor { - /** name of the character set to use for converting between characters and bytes */ - private static final String CHARSET_NAME = "UTF-8"; - - /** random number generator algorithm */ - private static final String RNG_ALGORITHM = "SHA1PRNG"; - - /** message digest algorithm (must be sufficiently long to provide the key and initialization vector) */ - private static final String DIGEST_ALGORITHM = "SHA-256"; - - /** key algorithm (must be compatible with CIPHER_ALGORITHM) */ - private static final String KEY_ALGORITHM = "AES"; - - /** cipher algorithm (must be compatible with KEY_ALGORITHM) */ - private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; - - - /** - * Private constructor that should never be called. - */ - private Encryptor() - {} - - - /** - * Encrypt the specified cleartext using the given password. - * With the correct salt, number of iterations, and password, the decrypt() method reverses - * the effect of this method. - * This method generates and uses a random salt, and the user-specified number of iterations - * and password to create a 16-byte secret key and 16-byte initialization vector. - * The secret key and initialization vector are then used in the AES-128 cipher to encrypt - * the given cleartext. - * - * @param salt - * salt that was used in the encryption (to be populated) - * @param iterations - * number of iterations to use in salting - * @param password - * password to be used for encryption - * @param cleartext - * cleartext to be encrypted - * @return - * ciphertext - * @throws Exception - * on any error encountered in encryption - */ - public static byte[] encrypt( - final byte[] salt, - final int iterations, - final String password, - final byte[] cleartext) - throws Exception - { - /* generate salt randomly */ - SecureRandom.getInstance(RNG_ALGORITHM).nextBytes(salt); - - /* compute key and initialization vector */ - final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); - byte[] pw = password.getBytes(CHARSET_NAME); - - for (int i = 0; i < iterations; i++) - { - /* add salt */ - final byte[] salted = new byte[pw.length + salt.length]; - System.arraycopy(pw, 0, salted, 0, pw.length); - System.arraycopy(salt, 0, salted, pw.length, salt.length); - Arrays.fill(pw, (byte) 0x00); - - /* compute SHA-256 digest */ - shaDigest.reset(); - pw = shaDigest.digest(salted); - Arrays.fill(salted, (byte) 0x00); - } - - /* extract the 16-byte key and initialization vector from the SHA-256 digest */ - final byte[] key = new byte[16]; - final byte[] iv = new byte[16]; - System.arraycopy(pw, 0, key, 0, 16); - System.arraycopy(pw, 16, iv, 0, 16); - Arrays.fill(pw, (byte) 0x00); - - /* perform AES-128 encryption */ - final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - - cipher.init( - Cipher.ENCRYPT_MODE, - new SecretKeySpec(key, KEY_ALGORITHM), - new IvParameterSpec(iv)); - - Arrays.fill(key, (byte) 0x00); - Arrays.fill(iv, (byte) 0x00); - - return cipher.doFinal(cleartext); - } - - - /** - * Decrypt the specified ciphertext using the given password. - * With the correct salt, number of iterations, and password, this method reverses the effect - * of the encrypt() method. - * This method uses the user-specified salt, number of iterations, and password - * to recreate the 16-byte secret key and 16-byte initialization vector. - * The secret key and initialization vector are then used in the AES-128 cipher to decrypt - * the given ciphertext. - * - * @param salt - * salt to be used in decryption - * @param iterations - * number of iterations to use in salting - * @param password - * password to be used for decryption - * @param ciphertext - * ciphertext to be decrypted - * @return - * cleartext - * @throws Exception - * on any error encountered in decryption - */ - public static byte[] decrypt( - final byte[] salt, - final int iterations, - final String password, - final byte[] ciphertext) - throws Exception - { - /* compute key and initialization vector */ - final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); - byte[] pw = password.getBytes(CHARSET_NAME); - - for (int i = 0; i < iterations; i++) - { - /* add salt */ - final byte[] salted = new byte[pw.length + salt.length]; - System.arraycopy(pw, 0, salted, 0, pw.length); - System.arraycopy(salt, 0, salted, pw.length, salt.length); - Arrays.fill(pw, (byte) 0x00); - - /* compute SHA-256 digest */ - shaDigest.reset(); - pw = shaDigest.digest(salted); - Arrays.fill(salted, (byte) 0x00); - } - - /* extract the 16-byte key and initialization vector from the SHA-256 digest */ - final byte[] key = new byte[16]; - final byte[] iv = new byte[16]; - System.arraycopy(pw, 0, key, 0, 16); - System.arraycopy(pw, 16, iv, 0, 16); - Arrays.fill(pw, (byte) 0x00); - - /* perform AES-128 decryption */ - final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - - cipher.init( - Cipher.DECRYPT_MODE, - new SecretKeySpec(key, KEY_ALGORITHM), - new IvParameterSpec(iv)); - - Arrays.fill(key, (byte) 0x00); - Arrays.fill(iv, (byte) 0x00); - - return cipher.doFinal(ciphertext); - } -} + /** name of the character set to use for converting between characters and bytes */ + private static final String CHARSET_NAME = "UTF-8"; + + /** random number generator algorithm */ + private static final String RNG_ALGORITHM = "SHA1PRNG"; + + /** message digest algorithm (must be sufficiently long to provide the key and initialization vector) */ + private static final String DIGEST_ALGORITHM = "SHA-256"; + + /** key algorithm (must be compatible with CIPHER_ALGORITHM) */ + private static final String KEY_ALGORITHM = "AES"; + + /** cipher algorithm (must be compatible with KEY_ALGORITHM) */ + private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; + + + /** + * Private constructor that should never be called. + */ + private Encryptor() + {} + + + /** + * Encrypt the specified cleartext using the given password. + * With the correct salt, number of iterations, and password, the decrypt() method reverses + * the effect of this method. + * This method generates and uses a random salt, and the user-specified number of iterations + * and password to create a 16-byte secret key and 16-byte initialization vector. + * The secret key and initialization vector are then used in the AES-128 cipher to encrypt + * the given cleartext. + * + * @param salt + * salt that was used in the encryption (to be populated) + * @param iterations + * number of iterations to use in salting + * @param password + * password to be used for encryption + * @param cleartext + * cleartext to be encrypted + * @return + * ciphertext + * @throws Exception + * on any error encountered in encryption + */ + public static byte[] encrypt( + final byte[] salt, + final int iterations, + final String password, + final byte[] cleartext) + throws Exception + { + /* generate salt randomly */ + SecureRandom.getInstance(RNG_ALGORITHM).nextBytes(salt); + + /* compute key and initialization vector */ + final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] pw = password.getBytes(CHARSET_NAME); + + for (int i = 0; i < iterations; i++) + { + /* add salt */ + final byte[] salted = new byte[pw.length + salt.length]; + System.arraycopy(pw, 0, salted, 0, pw.length); + System.arraycopy(salt, 0, salted, pw.length, salt.length); + Arrays.fill(pw, (byte) 0x00); + + /* compute SHA-256 digest */ + shaDigest.reset(); + pw = shaDigest.digest(salted); + Arrays.fill(salted, (byte) 0x00); + } + + /* extract the 16-byte key and initialization vector from the SHA-256 digest */ + final byte[] key = new byte[16]; + final byte[] iv = new byte[16]; + System.arraycopy(pw, 0, key, 0, 16); + System.arraycopy(pw, 16, iv, 0, 16); + Arrays.fill(pw, (byte) 0x00); + + /* perform AES-128 encryption */ + final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + + cipher.init( + Cipher.ENCRYPT_MODE, + new SecretKeySpec(key, KEY_ALGORITHM), + new IvParameterSpec(iv)); + + Arrays.fill(key, (byte) 0x00); + Arrays.fill(iv, (byte) 0x00); + + return cipher.doFinal(cleartext); + } + + + /** + * Decrypt the specified ciphertext using the given password. + * With the correct salt, number of iterations, and password, this method reverses the effect + * of the encrypt() method. + * This method uses the user-specified salt, number of iterations, and password + * to recreate the 16-byte secret key and 16-byte initialization vector. + * The secret key and initialization vector are then used in the AES-128 cipher to decrypt + * the given ciphertext. + * + * @param salt + * salt to be used in decryption + * @param iterations + * number of iterations to use in salting + * @param password + * password to be used for decryption + * @param ciphertext + * ciphertext to be decrypted + * @return + * cleartext + * @throws Exception + * on any error encountered in decryption + */ + public static byte[] decrypt( + final byte[] salt, + final int iterations, + final String password, + final byte[] ciphertext) + throws Exception + { + /* compute key and initialization vector */ + final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] pw = password.getBytes(CHARSET_NAME); + + for (int i = 0; i < iterations; i++) + { + /* add salt */ + final byte[] salted = new byte[pw.length + salt.length]; + System.arraycopy(pw, 0, salted, 0, pw.length); + System.arraycopy(salt, 0, salted, pw.length, salt.length); + Arrays.fill(pw, (byte) 0x00); + + /* compute SHA-256 digest */ + shaDigest.reset(); + pw = shaDigest.digest(salted); + Arrays.fill(salted, (byte) 0x00); + } + + /* extract the 16-byte key and initialization vector from the SHA-256 digest */ + final byte[] key = new byte[16]; + final byte[] iv = new byte[16]; + System.arraycopy(pw, 0, key, 0, 16); + System.arraycopy(pw, 16, iv, 0, 16); + Arrays.fill(pw, (byte) 0x00); + + /* perform AES-128 decryption */ + final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + + cipher.init( + Cipher.DECRYPT_MODE, + new SecretKeySpec(key, KEY_ALGORITHM), + new IvParameterSpec(iv)); + + Arrays.fill(key, (byte) 0x00); + Arrays.fill(iv, (byte) 0x00); + + return cipher.doFinal(ciphertext); + } +} diff --git a/src/org/connectbot/util/EntropyDialog.java b/src/org/connectbot/util/EntropyDialog.java index c6dc4e1..bb84418 100644 --- a/src/org/connectbot/util/EntropyDialog.java +++ b/src/org/connectbot/util/EntropyDialog.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -30,13 +30,13 @@ public class EntropyDialog extends Dialog implements OnEntropyGatheredListener { this.setContentView(R.layout.dia_gatherentropy); this.setTitle(R.string.pubkey_gather_entropy); - + ((EntropyView) findViewById(R.id.entropy)).addOnEntropyGatheredListener(this); } - + public EntropyDialog(Context context, View view) { super(context); - + this.setContentView(view); this.setTitle(R.string.pubkey_gather_entropy); diff --git a/src/org/connectbot/util/EntropyView.java b/src/org/connectbot/util/EntropyView.java index ff6f5ce..778a89a 100644 --- a/src/org/connectbot/util/EntropyView.java +++ b/src/org/connectbot/util/EntropyView.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -31,113 +31,113 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; -public class EntropyView extends View { +public class EntropyView extends View { private Paint mPaint; private FontMetrics mFontMetrics; private boolean mFlipFlop; private long mLastTime; private Vector<OnEntropyGatheredListener> listeners; - + private byte[] mEntropy; private int mEntropyIdx; private int splitText = 0; - + private float lastX = 0.0f, lastY = 0.0f; - + public EntropyView(Context context) { super(context); - + setUpEntropy(); } - + public EntropyView(Context context, AttributeSet attrs) { super(context, attrs); - + setUpEntropy(); } - + private void setUpEntropy() { - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setTypeface(Typeface.DEFAULT); - mPaint.setTextAlign(Paint.Align.CENTER); - mPaint.setTextSize(16); - mPaint.setColor(Color.WHITE); - mFontMetrics = mPaint.getFontMetrics(); - - mEntropy = new byte[20]; - mEntropyIdx = 0; - - listeners = new Vector<OnEntropyGatheredListener>(); + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setTypeface(Typeface.DEFAULT); + mPaint.setTextAlign(Paint.Align.CENTER); + mPaint.setTextSize(16); + mPaint.setColor(Color.WHITE); + mFontMetrics = mPaint.getFontMetrics(); + + mEntropy = new byte[20]; + mEntropyIdx = 0; + + listeners = new Vector<OnEntropyGatheredListener>(); } public void addOnEntropyGatheredListener(OnEntropyGatheredListener listener) { listeners.add(listener); } - + public void removeOnEntropyGatheredListener(OnEntropyGatheredListener listener) { listeners.remove(listener); } - - public void onDraw(Canvas c) { - String prompt = getResources().getString(R.string.pubkey_touch_prompt) - + " " + (int)(100.0 * (mEntropyIdx / 20.0)) + "% done"; - if (splitText > 0 || - mPaint.measureText(prompt) > (getWidth() * 0.8)) { - if (splitText == 0) - splitText = prompt.indexOf(" ", prompt.length() / 2); - - c.drawText(prompt.substring(0, splitText), - getWidth() / 2.0f, - getHeight() / 2.0f + (mPaint.ascent() + mPaint.descent()), - mPaint); - c.drawText(prompt.substring(splitText), - getWidth() / 2.0f, - getHeight() / 2.0f - (mPaint.ascent() + mPaint.descent()), - mPaint); - } else { - c.drawText(prompt, - getWidth() / 2.0f, - getHeight() / 2.0f - (mFontMetrics.ascent + mFontMetrics.descent) / 2, - mPaint); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mEntropyIdx >= 20 - || lastX == event.getX() - || lastY == event.getY()) - return true; - - // Only get entropy every 200 milliseconds to ensure the user has moved around. - long now = System.currentTimeMillis(); - if ((now - mLastTime) < 200) - return true; - else - mLastTime = now; - - // Get the lowest 4 bits of each X, Y input and concat to the entropy-gathering - // string. - if (mFlipFlop) - mEntropy[mEntropyIdx++] += (byte)((((int)event.getX() & 0x0F) << 4) | ((int)event.getY() & 0x0F)); - else - mEntropy[mEntropyIdx++] += (byte)((((int)event.getY() & 0x0F) << 4) | ((int)event.getX() & 0x0F)); - - mFlipFlop = !mFlipFlop; - lastX = event.getX(); - lastY = event.getY(); - - // SHA1PRNG only keeps 20 bytes (160 bits) of entropy. - if (mEntropyIdx >= 20) { - for (OnEntropyGatheredListener listener: listeners) { - listener.onEntropyGathered(mEntropy); - } - } - - invalidate(); - - return true; - } + + public void onDraw(Canvas c) { + String prompt = getResources().getString(R.string.pubkey_touch_prompt) + + " " + (int)(100.0 * (mEntropyIdx / 20.0)) + "% done"; + if (splitText > 0 || + mPaint.measureText(prompt) > (getWidth() * 0.8)) { + if (splitText == 0) + splitText = prompt.indexOf(" ", prompt.length() / 2); + + c.drawText(prompt.substring(0, splitText), + getWidth() / 2.0f, + getHeight() / 2.0f + (mPaint.ascent() + mPaint.descent()), + mPaint); + c.drawText(prompt.substring(splitText), + getWidth() / 2.0f, + getHeight() / 2.0f - (mPaint.ascent() + mPaint.descent()), + mPaint); + } else { + c.drawText(prompt, + getWidth() / 2.0f, + getHeight() / 2.0f - (mFontMetrics.ascent + mFontMetrics.descent) / 2, + mPaint); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mEntropyIdx >= 20 + || lastX == event.getX() + || lastY == event.getY()) + return true; + + // Only get entropy every 200 milliseconds to ensure the user has moved around. + long now = System.currentTimeMillis(); + if ((now - mLastTime) < 200) + return true; + else + mLastTime = now; + + // Get the lowest 4 bits of each X, Y input and concat to the entropy-gathering + // string. + if (mFlipFlop) + mEntropy[mEntropyIdx++] += (byte)((((int)event.getX() & 0x0F) << 4) | ((int)event.getY() & 0x0F)); + else + mEntropy[mEntropyIdx++] += (byte)((((int)event.getY() & 0x0F) << 4) | ((int)event.getX() & 0x0F)); + + mFlipFlop = !mFlipFlop; + lastX = event.getX(); + lastY = event.getY(); + + // SHA1PRNG only keeps 20 bytes (160 bits) of entropy. + if (mEntropyIdx >= 20) { + for (OnEntropyGatheredListener listener: listeners) { + listener.onEntropyGathered(mEntropy); + } + } + + invalidate(); + + return true; + } } diff --git a/src/org/connectbot/util/HelpTopicView.java b/src/org/connectbot/util/HelpTopicView.java index 84216ae..2619699 100644 --- a/src/org/connectbot/util/HelpTopicView.java +++ b/src/org/connectbot/util/HelpTopicView.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -43,20 +43,20 @@ public class HelpTopicView extends WebView { super(context); initialize(); } - + private void initialize() { WebSettings wSet = getSettings(); wSet.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); wSet.setUseWideViewPort(false); } - + public HelpTopicView setTopic(String topic) { String path = String.format("file:///android_asset/%s/%s%s", HelpActivity.HELPDIR, topic, HelpActivity.SUFFIX); loadUrl(path); - + computeScroll(); - + return this; } } diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index 550f88e..2575355 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -36,16 +36,16 @@ import android.util.Log; /** * Contains information about various SSH hosts, include public hostkey if known * from previous sessions. - * + * * @author jsharkey */ public class HostDatabase extends SQLiteOpenHelper { - + public final static String TAG = HostDatabase.class.toString(); - + public final static String DB_NAME = "hosts"; public final static int DB_VERSION = 15; - + public final static String TABLE_HOSTS = "hosts"; public final static String FIELD_HOST_NICKNAME = "nickname"; public final static String FIELD_HOST_USERNAME = "username"; @@ -61,7 +61,7 @@ public class HostDatabase extends SQLiteOpenHelper { public final static String FIELD_HOST_WANTSESSION = "wantsession"; public final static String FIELD_HOST_COMPRESSION = "compression"; public final static String FIELD_HOST_ENCODING = "encoding"; - + public final static String TABLE_PORTFORWARDS = "portforwards"; public final static String FIELD_PORTFORWARD_HOSTID = "hostid"; public final static String FIELD_PORTFORWARD_NICKNAME = "nickname"; @@ -74,19 +74,19 @@ public class HostDatabase extends SQLiteOpenHelper { public final static String COLOR_GREEN = "green"; public final static String COLOR_BLUE = "blue"; public final static String COLOR_GRAY = "gray"; - + public final static String PORTFORWARD_LOCAL = "local"; public final static String PORTFORWARD_REMOTE = "remote"; public final static String PORTFORWARD_DYNAMIC4 = "dynamic4"; public final static String PORTFORWARD_DYNAMIC5 = "dynamic5"; - + public final static String ENCODING_ASCII = "ASCII"; public final static String ENCODING_UTF8 = "UTF-8"; public final static String ENCODING_ISO88591 = "ISO8859_1"; public final static long PUBKEYID_NEVER = -2; public final static long PUBKEYID_ANY = -1; - + public HostDatabase(Context context) { super(context, DB_NAME, null, DB_VERSION); } @@ -113,7 +113,7 @@ public class HostDatabase extends SQLiteOpenHelper { //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); - + db.execSQL("CREATE TABLE " + TABLE_PORTFORWARDS + " (_id INTEGER PRIMARY KEY, " + FIELD_PORTFORWARD_HOSTID + " INTEGER, " @@ -133,7 +133,7 @@ public class HostDatabase extends SQLiteOpenHelper { onCreate(db); return; } - + switch (oldVersion) { case 10: db.execSQL("ALTER TABLE " + TABLE_HOSTS @@ -158,7 +158,7 @@ public class HostDatabase extends SQLiteOpenHelper { + " ADD COLUMN " + FIELD_HOST_ENCODING + " TEXT DEFAULT '" + ENCODING_ASCII + "'"); } } - + /** * Touch a specific host to update its "last connected" field. * @param nickname Nickname field of host to update @@ -166,41 +166,41 @@ public class HostDatabase extends SQLiteOpenHelper { 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, "_id = ?", new String[] { String.valueOf(host.getId()) }); - + db.close(); } - + /** * Create a new host using the given parameters. */ public HostBean saveHost(HostBean host) { SQLiteDatabase db = this.getWritableDatabase(); - + long id = db.insert(TABLE_HOSTS, null, host.getValues()); db.close(); host.setId(id); - + return host; } - + /** * Delete a specific host by its <code>_id</code> value. */ public void deleteHost(HostBean host) { if (host.getId() < 0) return; - + SQLiteDatabase db = this.getWritableDatabase(); 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. @@ -208,9 +208,9 @@ public class HostDatabase extends SQLiteOpenHelper { public List<HostBean> getHosts(boolean sortColors) { String sortField = sortColors ? FIELD_HOST_COLOR : FIELD_HOST_NICKNAME; SQLiteDatabase db = this.getReadableDatabase(); - + List<HostBean> hosts = new LinkedList<HostBean>(); - + Cursor c = db.query(TABLE_HOSTS, null, null, null, null, null, sortField + " ASC"); final int COL_ID = c.getColumnIndexOrThrow("_id"), @@ -226,10 +226,10 @@ public class HostDatabase extends SQLiteOpenHelper { COL_WANTSESSION = c.getColumnIndexOrThrow(FIELD_HOST_WANTSESSION), COL_COMPRESSION = c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION), COL_ENCODING = c.getColumnIndexOrThrow(FIELD_HOST_ENCODING); - + 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)); @@ -243,16 +243,16 @@ public class HostDatabase extends SQLiteOpenHelper { host.setWantSession(Boolean.valueOf(c.getString(COL_WANTSESSION))); host.setCompression(Boolean.valueOf(c.getString(COL_COMPRESSION))); host.setEncoding(c.getString(COL_ENCODING)); - + hosts.add(host); } - + c.close(); db.close(); - + return hosts; } - + /** * @param nickname * @param username @@ -263,7 +263,7 @@ public class HostDatabase extends SQLiteOpenHelper { public HostBean findHost(String nickname, String username, String hostname, int port) { SQLiteDatabase db = this.getReadableDatabase(); - + Cursor c = db.query(TABLE_HOSTS, null, FIELD_HOST_NICKNAME + " = ? AND " + FIELD_HOST_USERNAME + " = ? AND " + @@ -271,18 +271,18 @@ public class HostDatabase extends SQLiteOpenHelper { FIELD_HOST_PORT + " = ?", new String[] { nickname, username, hostname, String.valueOf(port) }, null, null, null); - + HostBean host = null; - + if (c != null) { if (c.moveToFirst()) host = createHostBean(c); - + c.close(); } - + db.close(); - + return host; } @@ -292,28 +292,28 @@ public class HostDatabase extends SQLiteOpenHelper { */ public HostBean findHostById(long hostId) { SQLiteDatabase db = this.getReadableDatabase(); - + Cursor c = db.query(TABLE_HOSTS, null, "_id = ?", new String[] { String.valueOf(hostId) }, null, null, null); - + HostBean host = null; - + if (c != null) { if (c.moveToFirst()) host = createHostBean(c); - + c.close(); } - + db.close(); - + 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))); @@ -327,39 +327,39 @@ public class HostDatabase extends SQLiteOpenHelper { host.setWantSession(Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_WANTSESSION)))); host.setCompression(Boolean.valueOf(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION)))); host.setEncoding(c.getString(c.getColumnIndexOrThrow(FIELD_HOST_ENCODING))); - + return host; } /** * Record the given hostkey into database under this nickname. * @param hostname - * @param port + * @param port * @param hostkeyalgo * @param 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 + " = ? AND " + FIELD_HOST_PORT + " = ?", new String[] { hostname, String.valueOf(port) }); Log.d(TAG, String.format("Finished saving hostkey information for '%s'", hostname)); - + db.close(); } - + /** * Build list of known hosts for Trilead library. * @return */ public KnownHosts getKnownHosts() { KnownHosts known = new KnownHosts(); - + SQLiteDatabase db = this.getReadableDatabase(); Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_HOSTNAME, FIELD_HOST_PORT, FIELD_HOST_HOSTKEYALGO, FIELD_HOST_HOSTKEY }, @@ -370,28 +370,28 @@ public class HostDatabase extends SQLiteOpenHelper { COL_PORT = c.getColumnIndexOrThrow(FIELD_HOST_PORT), 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); 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); } } - + c.close(); } - + db.close(); - + return known; } @@ -401,31 +401,31 @@ public class HostDatabase extends SQLiteOpenHelper { */ public void stopUsingPubkey(long pubkeyId) { if (pubkeyId < 0) return; - + SQLiteDatabase db = this.getWritableDatabase(); - + ContentValues values = new ContentValues(); values.put(FIELD_HOST_PUBKEYID, PUBKEYID_ANY); - + db.update(TABLE_HOSTS, values, FIELD_HOST_PUBKEYID + " = ?", new String[] { String.valueOf(pubkeyId) }); db.close(); - + Log.d(TAG, String.format("Set all hosts using pubkey id %d to -1", pubkeyId)); } - + /* * Methods for dealing with port forwards attached to hosts */ - + /** * Returns a list of all the port forwards associated with a particular host ID. * @param host the host for which we want the port forward list - * @return port forwards associated with host ID + * @return port forwards associated with host ID */ public List<PortForwardBean> getPortForwardsForHost(HostBean host) { SQLiteDatabase db = this.getReadableDatabase(); List<PortForwardBean> portForwards = new LinkedList<PortForwardBean>(); - + Cursor c = db.query(TABLE_PORTFORWARDS, new String[] { "_id", FIELD_PORTFORWARD_NICKNAME, FIELD_PORTFORWARD_TYPE, FIELD_PORTFORWARD_SOURCEPORT, FIELD_PORTFORWARD_DESTADDR, FIELD_PORTFORWARD_DESTPORT }, @@ -443,10 +443,10 @@ public class HostDatabase extends SQLiteOpenHelper { c.getInt(5)); portForwards.add(pfb); } - + c.close(); db.close(); - + return portForwards; } @@ -456,20 +456,20 @@ public class HostDatabase extends SQLiteOpenHelper { * @return true on success */ public boolean savePortForward(PortForwardBean pfb) { - boolean success = false; + boolean success = false; SQLiteDatabase db = this.getWritableDatabase(); if (pfb.getId() < 0) { long id = db.insert(TABLE_PORTFORWARDS, null, pfb.getValues()); pfb.setId(id); success = true; - } else { + } else { if (db.update(TABLE_PORTFORWARDS, pfb.getValues(), "_id = ?", new String[] { String.valueOf(pfb.getId()) }) > 0) success = true; } - + db.close(); - + return success; } @@ -480,7 +480,7 @@ public class HostDatabase extends SQLiteOpenHelper { public void deletePortForward(PortForwardBean pfb) { if (pfb.getId() < 0) return; - + SQLiteDatabase db = this.getWritableDatabase(); db.delete(TABLE_PORTFORWARDS, "_id = ?", new String[] { String.valueOf(pfb.getId()) }); db.close(); diff --git a/src/org/connectbot/util/OnEntropyGatheredListener.java b/src/org/connectbot/util/OnEntropyGatheredListener.java index 9dd2e44..6c31747 100644 --- a/src/org/connectbot/util/OnEntropyGatheredListener.java +++ b/src/org/connectbot/util/OnEntropyGatheredListener.java @@ -1,17 +1,17 @@ /* 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/>. */ diff --git a/src/org/connectbot/util/PubkeyDatabase.java b/src/org/connectbot/util/PubkeyDatabase.java index 9dd24f8..eb437ef 100644 --- a/src/org/connectbot/util/PubkeyDatabase.java +++ b/src/org/connectbot/util/PubkeyDatabase.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -32,15 +32,15 @@ import android.database.sqlite.SQLiteOpenHelper; /** * Public Key Encryption database. Contains private and public key pairs * for public key authentication. - * + * * @author Kenny Root */ -public class PubkeyDatabase extends SQLiteOpenHelper { +public class PubkeyDatabase extends SQLiteOpenHelper { public final static String TAG = PubkeyDatabase.class.toString(); - + public final static String DB_NAME = "pubkeys"; public final static int DB_VERSION = 1; - + public final static String TABLE_PUBKEYS = "pubkeys"; public final static String FIELD_PUBKEY_NICKNAME = "nickname"; public final static String FIELD_PUBKEY_TYPE = "type"; @@ -48,16 +48,16 @@ public class PubkeyDatabase extends SQLiteOpenHelper { public final static String FIELD_PUBKEY_PUBLIC = "public"; 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) { super(context, DB_NAME, null, DB_VERSION); - + this.context = context; } @@ -77,7 +77,7 @@ public class PubkeyDatabase extends SQLiteOpenHelper { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } - + /** * Delete a specific host by its <code>_id</code> value. */ @@ -85,12 +85,12 @@ public class PubkeyDatabase extends SQLiteOpenHelper { HostDatabase hostdb = new HostDatabase(context); hostdb.stopUsingPubkey(pubkey.getId()); hostdb.close(); - + SQLiteDatabase db = getWritableDatabase(); db.delete(TABLE_PUBKEYS, "_id = ?", new String[] { Long.toString(pubkey.getId()) }); db.close(); } - + /** * Return a cursor that contains information about all known hosts. */ @@ -102,22 +102,22 @@ public class PubkeyDatabase extends SQLiteOpenHelper { FIELD_PUBKEY_PUBLIC, FIELD_PUBKEY_ENCRYPTED, FIELD_PUBKEY_STARTUP }, null, null, null, null, null); }*/ - + public List<PubkeyBean> allPubkeys() { return getPubkeys(null, null); } - + public List<PubkeyBean> getAllStartPubkeys() { return getPubkeys(FIELD_PUBKEY_STARTUP + " = 1 AND " + FIELD_PUBKEY_ENCRYPTED + " = 0", null); } - + private List<PubkeyBean> getPubkeys(String selection, String[] selectionArgs) { SQLiteDatabase db = getReadableDatabase(); List<PubkeyBean> pubkeys = new LinkedList<PubkeyBean>(); Cursor c = db.query(TABLE_PUBKEYS, null, selection, selectionArgs, null, null, null); - + if (c != null) { final int COL_ID = c.getColumnIndexOrThrow("_id"), COL_NICKNAME = c.getColumnIndexOrThrow(FIELD_PUBKEY_NICKNAME), @@ -126,10 +126,10 @@ public class PubkeyDatabase extends SQLiteOpenHelper { COL_PUBLIC = c.getColumnIndexOrThrow(FIELD_PUBKEY_PUBLIC), COL_ENCRYPTED = c.getColumnIndexOrThrow(FIELD_PUBKEY_ENCRYPTED), COL_STARTUP = c.getColumnIndexOrThrow(FIELD_PUBKEY_STARTUP); - + while (c.moveToNext()) { PubkeyBean pubkey = new PubkeyBean(); - + pubkey.setId(c.getLong(COL_ID)); pubkey.setNickname(c.getString(COL_NICKNAME)); pubkey.setType(c.getString(COL_TYPE)); @@ -137,46 +137,46 @@ public class PubkeyDatabase extends SQLiteOpenHelper { pubkey.setPublicKey(c.getBlob(COL_PUBLIC)); pubkey.setEncrypted(c.getInt(COL_ENCRYPTED) > 0); pubkey.setStartup(c.getInt(COL_STARTUP) > 0); - + pubkeys.add(pubkey); } - + c.close(); } - + db.close(); - + return pubkeys; } - + /** * @param hostId * @return */ public PubkeyBean findPubkeyById(long pubkeyId) { SQLiteDatabase db = getReadableDatabase(); - + Cursor c = db.query(TABLE_PUBKEYS, null, "_id = ?", new String[] { String.valueOf(pubkeyId) }, null, null, null); - + PubkeyBean pubkey = null; - + if (c != null) { if (c.moveToFirst()) pubkey = createPubkeyBean(c); - + c.close(); } - + db.close(); - + return pubkey; } - + private PubkeyBean createPubkeyBean(Cursor c) { PubkeyBean pubkey = new PubkeyBean(); - + pubkey.setId(c.getLong(c.getColumnIndexOrThrow("_id"))); pubkey.setNickname(c.getString(c.getColumnIndexOrThrow(FIELD_PUBKEY_NICKNAME))); pubkey.setType(c.getString(c.getColumnIndexOrThrow(FIELD_PUBKEY_TYPE))); @@ -184,7 +184,7 @@ public class PubkeyDatabase extends SQLiteOpenHelper { pubkey.setPublicKey(c.getBlob(c.getColumnIndexOrThrow(FIELD_PUBKEY_PUBLIC))); pubkey.setEncrypted(c.getInt(c.getColumnIndexOrThrow(FIELD_PUBKEY_ENCRYPTED)) > 0); pubkey.setStartup(c.getInt(c.getColumnIndexOrThrow(FIELD_PUBKEY_STARTUP)) > 0); - + return pubkey; } @@ -194,68 +194,68 @@ public class PubkeyDatabase extends SQLiteOpenHelper { */ 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"); - + if (c != null) { int COL = c.getColumnIndexOrThrow(column); - + while (c.moveToNext()) list.add(c.getString(COL)); - + c.close(); } - + db.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) { if (c.moveToFirst()) nickname = c.getString(c.getColumnIndexOrThrow(FIELD_PUBKEY_NICKNAME)); - + c.close(); } - + db.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(); - + SQLiteDatabase db = this.getWritableDatabase(); + Cursor c = db.query(TABLE_PUBKEYS, new String[] { FIELD_PUBKEY_TYPE, FIELD_PUBKEY_PRIVATE, FIELD_PUBKEY_ENCRYPTED }, "_id = ?", new String[] { String.valueOf(id) }, null, null, null); - + if (!c.moveToFirst()) return false; - + String keyType = c.getString(0); byte[] encPriv = c.getBlob(1); c.close(); @@ -270,12 +270,12 @@ public class PubkeyDatabase extends SQLiteOpenHelper { } catch (InvalidKeySpecException e) { return false; } - + ContentValues values = new ContentValues(); values.put(FIELD_PUBKEY_PRIVATE, PubkeyUtils.getEncodedPrivate(priv, newPassword)); values.put(FIELD_PUBKEY_ENCRYPTED, newPassword.length() > 0 ? 1 : 0); db.update(TABLE_PUBKEYS, values, "_id = ?", new String[] { String.valueOf(id) }); - + return true; } */ @@ -286,22 +286,22 @@ public class PubkeyDatabase extends SQLiteOpenHelper { public PubkeyBean savePubkey(PubkeyBean pubkey) { SQLiteDatabase db = this.getWritableDatabase(); boolean success = false; - + ContentValues values = pubkey.getValues(); - + if (pubkey.getId() > 0) { values.remove("_id"); if (db.update(TABLE_PUBKEYS, values, "_id = ?", new String[] { String.valueOf(pubkey.getId()) }) > 0) success = true; } - + if (!success) { long id = db.insert(TABLE_PUBKEYS, null, pubkey.getValues()); pubkey.setId(id); } - + db.close(); - + return pubkey; } } diff --git a/src/org/connectbot/util/PubkeyUtils.java b/src/org/connectbot/util/PubkeyUtils.java index d6bf332..a564c5f 100644 --- a/src/org/connectbot/util/PubkeyUtils.java +++ b/src/org/connectbot/util/PubkeyUtils.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -65,13 +65,13 @@ import com.trilead.ssh2.signature.RSASHA1Verify; public class PubkeyUtils { public static final String PKCS8_START = "-----BEGIN PRIVATE KEY-----"; public static final String PKCS8_END = "-----END PRIVATE KEY-----"; - + // Size in bytes of salt to use. private static final int SALT_SIZE = 8; - + // Number of iterations for password hashing. PKCS#5 recommends 1000 private static final int ITERATIONS = 1000; - + public static String formatKey(Key key){ String algo = key.getAlgorithm(); String fmt = key.getFormat(); @@ -79,7 +79,7 @@ public class PubkeyUtils { return "Key[algorithm=" + algo + ", format=" + fmt + ", bytes=" + encoded.length + "]"; } - + public static String describeKey(Key key, boolean encrypted) { String desc = null; if (key instanceof RSAPublicKey) { @@ -90,24 +90,24 @@ public class PubkeyUtils { } else { desc = "Unknown Key Type"; } - + if (encrypted) desc += " (encrypted)"; - + return desc; } - + public static byte[] sha256(byte[] data) throws NoSuchAlgorithmException { return MessageDigest.getInstance("SHA-256").digest(data); } - + public static byte[] cipher(int mode, byte[] data, byte[] secret) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { SecretKeySpec secretKeySpec = new SecretKeySpec(sha256(secret), "AES"); Cipher c = Cipher.getInstance("AES"); c.init(mode, secretKeySpec); return c.doFinal(data); - } - + } + public static byte[] encrypt(byte[] cleartext, String secret) throws Exception { byte[] salt = new byte[SALT_SIZE]; @@ -123,15 +123,15 @@ public class PubkeyUtils { return complete; } - + public static byte[] decrypt(byte[] complete, String secret) throws Exception { try { byte[] salt = new byte[SALT_SIZE]; byte[] ciphertext = new byte[complete.length - salt.length]; - + System.arraycopy(complete, 0, salt, 0, salt.length); System.arraycopy(complete, salt.length, ciphertext, 0, ciphertext.length); - + return Encryptor.decrypt(salt, ITERATIONS, secret, ciphertext); } catch (Exception e) { Log.d("decrypt", "Could not decrypt with new method", e); @@ -139,41 +139,41 @@ public class PubkeyUtils { return cipher(Cipher.DECRYPT_MODE, complete, secret.getBytes()); } } - + public static byte[] getEncodedPublic(PublicKey pk) { return new X509EncodedKeySpec(pk.getEncoded()).getEncoded(); } - + public static byte[] getEncodedPrivate(PrivateKey pk) { return new PKCS8EncodedKeySpec(pk.getEncoded()).getEncoded(); } - + public static byte[] getEncodedPrivate(PrivateKey pk, String secret) throws Exception { if (secret.length() > 0) return encrypt(getEncodedPrivate(pk), secret); else return getEncodedPrivate(pk); } - + public static PrivateKey decodePrivate(byte[] encoded, String keyType) throws NoSuchAlgorithmException, InvalidKeySpecException { PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); KeyFactory kf = KeyFactory.getInstance(keyType); return kf.generatePrivate(privKeySpec); } - + public static PrivateKey decodePrivate(byte[] encoded, String keyType, String secret) throws Exception { if (secret != null && secret.length() > 0) return decodePrivate(decrypt(encoded, secret), keyType); else return decodePrivate(encoded, keyType); } - + public static PublicKey decodePublic(byte[] encoded, String keyType) throws NoSuchAlgorithmException, InvalidKeySpecException { X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encoded); KeyFactory kf = KeyFactory.getInstance(keyType); return kf.generatePublic(pubKeySpec); } - + public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, InvalidKeySpecException { KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); KeySpec pubKeySpec; @@ -208,11 +208,11 @@ public class PubkeyUtils { return new KeyPair(pub, priv); } - + /* * Trilead compatibility methods */ - + public static Object convertToTrilead(PublicKey pk) { if (pk instanceof RSAPublicKey) { return new com.trilead.ssh2.signature.RSAPublicKey( @@ -223,10 +223,10 @@ public class PubkeyUtils { return new com.trilead.ssh2.signature.DSAPublicKey( dp.getP(), dp.getQ(), dp.getG(), ((DSAPublicKey) pk).getY()); } - + throw new IllegalArgumentException("PublicKey is not RSA or DSA format"); } - + public static Object convertToTrilead(PrivateKey priv, PublicKey pub) { if (priv instanceof RSAPrivateKey) { return new com.trilead.ssh2.signature.RSAPrivateKey( @@ -239,14 +239,14 @@ public class PubkeyUtils { dp.getP(), dp.getQ(), dp.getG(), ((DSAPublicKey) pub).getY(), ((DSAPrivateKey) priv).getX()); } - + throw new IllegalArgumentException("Key is not RSA or DSA format"); } - + /* - * OpenSSH compatibility methods + * OpenSSH compatibility methods */ - + public static String convertToOpenSSHFormat(PublicKey pk, String nickname) throws IOException, InvalidKeyException { if (nickname == null) nickname = "connectbot@android"; @@ -262,11 +262,11 @@ public class PubkeyUtils { (com.trilead.ssh2.signature.DSAPublicKey)convertToTrilead(pk)))); return data + " " + nickname; } - + throw new InvalidKeyException("Unknown key type"); } - - public static String exportPEM(PrivateKey key, String secret) throws NoSuchAlgorithmException, InvalidParameterSpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException, IllegalBlockSizeException, IOException { + + public static String exportPEM(PrivateKey key, String secret) throws NoSuchAlgorithmException, InvalidParameterSpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException, IllegalBlockSizeException, IOException { StringBuilder sb = new StringBuilder(); byte[] data = key.getEncoded(); @@ -278,24 +278,24 @@ public class PubkeyUtils { byte[] salt = new byte[8]; SecureRandom random = new SecureRandom(); random.nextBytes(salt); - + PBEParameterSpec defParams = new PBEParameterSpec(salt, 1); AlgorithmParameters params = AlgorithmParameters.getInstance(key.getAlgorithm()); - + params.init(defParams); - + PBEKeySpec pbeSpec = new PBEKeySpec(secret.toCharArray()); - + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(key.getAlgorithm()); Cipher cipher = Cipher.getInstance(key.getAlgorithm()); cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), params); - + byte[] wrappedKey = cipher.wrap(key); - + EncryptedPrivateKeyInfo pinfo = new EncryptedPrivateKeyInfo(params, wrappedKey); - + data = pinfo.getEncoded(); - + sb.append("Proc-Type: 4,ENCRYPTED\n"); sb.append("DEK-Info: DES-EDE3-CBC,"); sb.append(encodeHex(salt)); @@ -314,18 +314,18 @@ public class PubkeyUtils { return sb.toString(); } - - final static private char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', + + final static private char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - private static String encodeHex(byte[] bytes) { - char[] hex = new char[bytes.length * 2]; - - int i = 0; - for (byte b : bytes) { - hex[i++] = hexDigit[(b >> 4) & 0x0f]; - hex[i++] = hexDigit[b & 0x0f]; - } - - return new String(hex); - } + private static String encodeHex(byte[] bytes) { + char[] hex = new char[bytes.length * 2]; + + int i = 0; + for (byte b : bytes) { + hex[i++] = hexDigit[(b >> 4) & 0x0f]; + hex[i++] = hexDigit[b & 0x0f]; + } + + return new String(hex); + } } diff --git a/src/org/connectbot/util/UpdateHelper.java b/src/org/connectbot/util/UpdateHelper.java index b5061e3..633b801 100644 --- a/src/org/connectbot/util/UpdateHelper.java +++ b/src/org/connectbot/util/UpdateHelper.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -45,34 +45,34 @@ import android.util.Log; * Helper class that checks for updates to this application. On construction, it * spawns a background thread that checks for any app updates. If available, * shows a dialog to the user, prompting them to visit Market for the upgrade. - * + * * <b>Be sure to change the UPDATE_URL field before using this class.</b> Then * place a text file at that URL containing JSON data in the format: - * + * * <code>{"versionCode": 110, "features": "Brand new interface with over * 9,000 improvements.", "target": "search?q=searchterms"}</code> - * + * * Which should contain information about your newest version. The * <code>target</code> field is used to build an Intent that launches Market on * the device, simply be prefixing it with <code>market://</code>. If you know * your exact Market ID, you could use the value * <code>details?id=yourexactmarketid</code> - * + * * If you're looking for an advanced version-checking system that offers more * customization, check out Veecheck: http://www.tomgibara.com/android/veecheck/ - * + * * @author jsharkey */ public final class UpdateHelper implements Runnable { - + public final static String TAG = UpdateHelper.class.toString(); public final static String UPDATE_URL = "http://connectbot.org/version"; protected Context context; - + private String packageName, versionName; protected int versionCode; - + private String userAgent; /** @@ -94,25 +94,25 @@ public final class UpdateHelper implements Runnable { } catch(Exception e) { Log.e(TAG, "Couldn't find package information in PackageManager", e); return; - + } - + // decide if we really need to check for update Resources res = context.getResources(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - + String frequency = prefs.getString(res.getString(R.string.pref_update), res.getString(R.string.list_update_daily)); long lastChecked = prefs.getLong(res.getString(R.string.pref_lastchecked), 0); long now = (System.currentTimeMillis() / 1000); long passed = now - lastChecked; - + boolean shouldCheck = false; if(frequency.equals(res.getString(R.string.list_update_daily))) { shouldCheck = (passed > 60 * 60 * 24); } else if(frequency.equals(res.getString(R.string.list_update_weekly))) { shouldCheck = (passed > 60 * 60 * 24 * 7); } - + // place version information in user-agent string to be used later this.userAgent = String.format("%s/%s (%d, freq=%s)", packageName, versionName, versionCode, frequency); @@ -120,12 +120,12 @@ public final class UpdateHelper implements Runnable { // spawn thread to check for update // Note that this class should be marked final because a thread is started in the constructor. new Thread(this).start(); - + // update our last-checked time Editor editor = prefs.edit(); editor.putLong(res.getString(R.string.pref_lastchecked), now); editor.commit(); - + } } @@ -136,13 +136,13 @@ public final class UpdateHelper implements Runnable { // pass information off to handler to create JSONObject json = new JSONObject(UpdateHelper.getUrl(UPDATE_URL, userAgent)); Message.obtain(versionHandler, -1, json).sendToTarget(); - + } catch(Exception e) { Log.e(TAG, "Problem while fetching/parsing update response", e); } } - + /** * Handler that will parse the JSON response and show dialog to user if an @@ -160,24 +160,24 @@ public final class UpdateHelper implements Runnable { final int versionCode = json.optInt("versionCode"); final String features = json.optString("features"); final String target = "market://" + json.optString("target"); - + // skip if we're already good enough if(versionCode <= UpdateHelper.this.versionCode) return; - + // build dialog to prompt user about updating new AlertDialog.Builder(context) .setTitle(R.string.upgrade) .setMessage(features) .setPositiveButton(R.string.upgrade_pos, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { + public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(target)); context.startActivity(intent); - } - }) - .setNegativeButton(R.string.upgrade_neg, null).create().show(); - + } + }) + .setNegativeButton(R.string.upgrade_neg, null).create().show(); + } - + }; @@ -186,17 +186,17 @@ public final class UpdateHelper implements Runnable { * downtime with a 6-second timeout. */ private static String getUrl(String tryUrl, String userAgent) throws Exception { - + URL url = new URL(tryUrl); URLConnection connection = url.openConnection(); connection.setConnectTimeout(6000); connection.setReadTimeout(6000); connection.setRequestProperty("User-Agent", userAgent); connection.connect(); - + InputStream is = connection.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream(); - + int bytesRead; byte[] buffer = new byte[1024]; while ((bytesRead = is.read(buffer)) != -1) { @@ -208,8 +208,8 @@ public final class UpdateHelper implements Runnable { is.close(); return new String(os.toByteArray()); - + } - + } diff --git a/src/org/connectbot/util/XmlBuilder.java b/src/org/connectbot/util/XmlBuilder.java index d2c2923..828d032 100644 --- a/src/org/connectbot/util/XmlBuilder.java +++ b/src/org/connectbot/util/XmlBuilder.java @@ -1,17 +1,17 @@ /* 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/>. */ @@ -25,31 +25,31 @@ import com.trilead.ssh2.crypto.Base64; */ public class XmlBuilder { private StringBuilder sb; - + public XmlBuilder() { sb = new StringBuilder(); } - + public XmlBuilder append(String data) { sb.append(data); - + return this; } - + public XmlBuilder append(String field, Object data) { if (data == null) { sb.append(String.format("<%s/>", field)); } else if (data instanceof String) { String input = (String) data; boolean binary = false; - + for (byte b : input.getBytes()) { if (b < 0x20 || b > 0x7e) { binary = true; break; } } - + sb.append(String.format("<%s>%s</%s>", field, binary ? new String(Base64.encode(input.getBytes())) : input, field)); } else if (data instanceof Integer) { @@ -61,10 +61,10 @@ public class XmlBuilder { } else if (data instanceof Boolean) { sb.append(String.format("<%s>%s</%s>", field, (Boolean) data, field)); } - + return this; } - + public String toString() { return sb.toString(); } |