aboutsummaryrefslogtreecommitdiffstats
path: root/src/org/theb/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/theb/ssh')
-rw-r--r--src/org/theb/ssh/ConnectionThread.java30
-rw-r--r--src/org/theb/ssh/FeedbackUI.java25
-rw-r--r--src/org/theb/ssh/HostDbProvider.java18
-rw-r--r--src/org/theb/ssh/HostEditor.java18
-rw-r--r--src/org/theb/ssh/HostsList.java18
-rw-r--r--src/org/theb/ssh/InteractiveHostKeyVerifier.java18
-rw-r--r--src/org/theb/ssh/JCTerminalView.java329
-rw-r--r--src/org/theb/ssh/JTATerminalView.java323
-rw-r--r--src/org/theb/ssh/PasswordDialog.java18
-rw-r--r--src/org/theb/ssh/PreferencesDialog.java42
-rw-r--r--src/org/theb/ssh/SecureShell.java370
-rw-r--r--src/org/theb/ssh/ShellView.java77
-rw-r--r--src/org/theb/ssh/Terminal.java31
-rw-r--r--src/org/theb/ssh/TrileadConnectionThread.java164
14 files changed, 1126 insertions, 355 deletions
diff --git a/src/org/theb/ssh/ConnectionThread.java b/src/org/theb/ssh/ConnectionThread.java
new file mode 100644
index 0000000..a201e62
--- /dev/null
+++ b/src/org/theb/ssh/ConnectionThread.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public abstract class ConnectionThread extends Thread {
+ public ConnectionThread(FeedbackUI ui, String hostname, String username, int port) {}
+ public abstract void finish();
+ public abstract InputStream getReader();
+ public abstract OutputStream getWriter();
+ public abstract void setPassword(String password);
+}
diff --git a/src/org/theb/ssh/FeedbackUI.java b/src/org/theb/ssh/FeedbackUI.java
new file mode 100644
index 0000000..ba4ae03
--- /dev/null
+++ b/src/org/theb/ssh/FeedbackUI.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+public interface FeedbackUI {
+ public void connectionLost(Throwable reason);
+ public void setWaiting(boolean isWaiting, String title, String message);
+ public void askPassword();
+}
diff --git a/src/org/theb/ssh/HostDbProvider.java b/src/org/theb/ssh/HostDbProvider.java
index 20ec224..f0eac73 100644
--- a/src/org/theb/ssh/HostDbProvider.java
+++ b/src/org/theb/ssh/HostDbProvider.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import java.util.HashMap;
diff --git a/src/org/theb/ssh/HostEditor.java b/src/org/theb/ssh/HostEditor.java
index d642fd4..967cf5b 100644
--- a/src/org/theb/ssh/HostEditor.java
+++ b/src/org/theb/ssh/HostEditor.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import org.theb.provider.HostDb;
diff --git a/src/org/theb/ssh/HostsList.java b/src/org/theb/ssh/HostsList.java
index 4d9e398..7114497 100644
--- a/src/org/theb/ssh/HostsList.java
+++ b/src/org/theb/ssh/HostsList.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import org.theb.provider.HostDb;
diff --git a/src/org/theb/ssh/InteractiveHostKeyVerifier.java b/src/org/theb/ssh/InteractiveHostKeyVerifier.java
index a461724..7c21f80 100644
--- a/src/org/theb/ssh/InteractiveHostKeyVerifier.java
+++ b/src/org/theb/ssh/InteractiveHostKeyVerifier.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import com.trilead.ssh2.ServerHostKeyVerifier;
diff --git a/src/org/theb/ssh/JCTerminalView.java b/src/org/theb/ssh/JCTerminalView.java
new file mode 100644
index 0000000..47d12aa
--- /dev/null
+++ b/src/org/theb/ssh/JCTerminalView.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PixelXorXfermode;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.jcraft.jcterm.Emulator;
+import com.jcraft.jcterm.EmulatorVT100;
+import com.jcraft.jcterm.Term;
+
+public class JCTerminalView extends View implements Term, Terminal {
+ private final Paint mPaint;
+ private Bitmap mBitmap;
+ private Canvas mCanvas;
+
+ private final Paint mCursorPaint;
+
+ private Emulator emulator = null;
+
+ private boolean mBold = false;
+ private boolean mUnderline = false;
+ private boolean mReverse = false;
+
+ private int mDefaultForeground = Color.WHITE;
+ private int mDefaultBackground = Color.BLACK;
+ private int mForeground = Color.WHITE;
+ private int mBackground = Color.BLACK;
+
+ private boolean mAntialias = true;
+
+ private int mTermWidth = 80;
+ private int mTermHeight = 24;
+
+ private int mCharHeight;
+ private int mCharWidth;
+ private int mDescent;
+
+
+ // Cursor location
+ private int x = 0;
+ private int y = 0;
+
+ private final Object[] mColors = {Color.BLACK, Color.RED, Color.GREEN, Color.YELLOW,
+ Color.BLUE, Color.MAGENTA, Color.CYAN, Color.WHITE};
+
+ public JCTerminalView(Context c) {
+ super(c);
+ mPaint = new Paint();
+ mPaint.setAntiAlias(mAntialias);
+ mPaint.setColor(mDefaultForeground);
+
+ mCursorPaint = new Paint();
+ mCursorPaint.setAntiAlias(mAntialias);
+ mCursorPaint.setColor(mDefaultForeground);
+ mCursorPaint.setXfermode(new PixelXorXfermode(mDefaultBackground));
+
+ setFont(Typeface.MONOSPACE);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+
+ if (mCharHeight > 0 && y > mCharHeight) {
+ // Invert pixels for cursor position.
+ canvas.drawRect(x, y - mCharHeight, x + mCharWidth, y, mCursorPaint);
+ }
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ Log.d("SSH/TerminalView", "onSizeChanged called");
+ Bitmap newBitmap = Bitmap.createBitmap(w, h, false);
+ Canvas newCanvas = new Canvas();
+
+ newCanvas.setDevice(newBitmap);
+
+ if (mBitmap != null)
+ newCanvas.drawBitmap(mBitmap, 0, 0, mPaint);
+
+ mBitmap = newBitmap;
+ mCanvas = newCanvas;
+
+ setSize(w, h);
+ }
+
+ private void setSize(int w, int h) {
+ int column = w / getCharWidth();
+ int row = h / getCharHeight();
+
+ mTermWidth = column;
+ mTermHeight = row;
+
+ if (emulator != null)
+ emulator.reset();
+
+ clear_area(0, 0, w, h);
+
+ // TODO: finish this method
+ }
+
+ private void setFont(Typeface typeface) {
+ mPaint.setTypeface(typeface);
+ mPaint.setTextSize(8);
+ FontMetricsInt fm = mPaint.getFontMetricsInt();
+ mDescent = fm.descent;
+
+ float[] widths = new float[1];
+ mPaint.getTextWidths("X", widths);
+ mCharWidth = (int)widths[0];
+
+ // Is this right?
+ mCharHeight = Math.abs(fm.top) + Math.abs(fm.descent);
+ Log.d("SSH", "character height is " + mCharHeight);
+ // mCharHeight += mLineSpace * 2;
+ // mDescent += mLineSpace;
+ }
+
+ public void beep() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void clear() {
+ mPaint.setColor(getBackgroundColor());
+ mCanvas.drawRect(0, 0, mCanvas.getBitmapWidth(),
+ mCanvas.getBitmapHeight(), mPaint);
+ mPaint.setColor(getForegroundColor());
+ }
+
+ private int getBackgroundColor() {
+ if (mReverse)
+ return mForeground;
+ return mBackground;
+ }
+
+ private int getForegroundColor() {
+ if (mReverse)
+ return mBackground;
+ return mForeground;
+ }
+
+ public void clear_area(int x1, int y1, int x2, int y2) {
+ mPaint.setColor(getBackgroundColor());
+ if (mCanvas != null)
+ mCanvas.drawRect(x1, y1, x2, y2, mPaint);
+ mPaint.setColor(getForegroundColor());
+ }
+
+ public void drawBytes(byte[] buf, int s, int len, int x, int y) {
+ String chars = null;
+ try {
+ chars = new String(buf, "ASCII");
+ drawString(chars.substring(s, s+len), x, y);
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ Log.e("SSH", "Can't convert bytes to ASCII");
+ }
+ }
+
+ public void drawString(String str, int x, int y) {
+ mPaint.setFakeBoldText(mBold);
+ mPaint.setUnderlineText(mUnderline);
+ mCanvas.drawText(str, x, y - mDescent, mPaint);
+ }
+
+ public void draw_cursor() {
+ postInvalidate();
+ }
+
+ public int getCharHeight() {
+ return mCharHeight;
+ }
+
+ public int getCharWidth() {
+ return mCharWidth;
+ }
+
+ public Object getColor(int index) {
+ if (mColors == null || index < 0 || mColors.length <= index)
+ return null;
+ return mColors[index];
+ }
+
+ public int getColumnCount() {
+ return mTermWidth;
+ }
+
+ public int getRowCount() {
+ return mTermHeight;
+ }
+
+ public int getTermHeight() {
+ return mTermHeight * mCharHeight;
+ }
+
+ public int getTermWidth() {
+ return mTermWidth * mCharWidth;
+ }
+
+ public void redraw(int x, int y, int width, int height) {
+ //invalidate(x, y, x+width, y+height);
+ postInvalidate();
+ }
+
+ public void resetAllAttributes() {
+ mBold = false;
+ mUnderline = false;
+ mReverse = false;
+
+ mBackground = mDefaultBackground;
+ mForeground = mDefaultForeground;
+
+ if (mPaint != null)
+ mPaint.setColor(mForeground);
+ }
+
+ public void scroll_area(int x, int y, int w, int h, int dx, int dy) {
+ // TODO: make scrolling more efficient (memory-wise)
+ mCanvas.drawBitmap(Bitmap.createBitmap(mBitmap, x, y, w, h), x+dx, y+dy, null);
+ }
+
+ private int toColor(Object o) {
+ if (o instanceof Integer) {
+ return ((Integer)o).intValue();
+ }
+
+ if (o instanceof String) {
+ return Color.parseColor((String)o);
+ }
+
+ return Color.WHITE;
+ }
+
+ public void setBackGround(Object background) {
+ mBackground = toColor(background);
+ }
+
+ public void setBold() {
+ mBold = true;
+ }
+
+ public void setCursor(int x, int y) {
+ // Make sure we don't go outside the bounds of the window.
+ this.x = Math.max(
+ Math.min(x, getWidth() - mCharWidth),
+ 0);
+ this.y = Math.max(
+ Math.min(y, getHeight()),
+ mCharHeight);
+ }
+
+ public void setDefaultBackGround(Object background) {
+ mDefaultBackground = toColor(background);
+ }
+
+ public void setDefaultForeGround(Object foreground) {
+ mDefaultForeground = toColor(foreground);
+ }
+
+ public void setForeGround(Object foreground) {
+ mForeground = toColor(foreground);
+ }
+
+ public void setReverse() {
+ mReverse = true;
+ if (mPaint != null)
+ mPaint.setColor(getForegroundColor());
+ }
+
+ public void setUnderline() {
+ mUnderline = true;
+ }
+
+ public void start(InputStream in, OutputStream out) {
+ emulator = new EmulatorVT100(this, in);
+ emulator.reset();
+ emulator.start();
+
+ clear();
+ }
+
+ public byte[] getKeyCode(int keyCode, int meta) {
+ if (keyCode == KeyEvent.KEYCODE_NEWLINE)
+ return emulator.getCodeENTER();
+ else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT)
+ return emulator.getCodeLEFT();
+ else if (keyCode == KeyEvent.KEYCODE_DPAD_UP)
+ return emulator.getCodeUP();
+ else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ return emulator.getCodeDOWN();
+ else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)
+ return emulator.getCodeRIGHT();
+ else
+ return null;
+ }
+}
diff --git a/src/org/theb/ssh/JTATerminalView.java b/src/org/theb/ssh/JTATerminalView.java
new file mode 100644
index 0000000..2238f02
--- /dev/null
+++ b/src/org/theb/ssh/JTATerminalView.java
@@ -0,0 +1,323 @@
+package org.theb.ssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import de.mud.terminal.SoftFont;
+import de.mud.terminal.VDUBuffer;
+import de.mud.terminal.VDUDisplay;
+import de.mud.terminal.vt320;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+public class JTATerminalView extends View implements VDUDisplay, Terminal, Runnable {
+ private Paint paint;
+ private Canvas canvas;
+ private Bitmap bitmap;
+
+ protected vt320 emulation;
+ private VDUBuffer buffer;
+
+ private InputStream in;
+ private OutputStream out;
+
+ private String encoding = "ASCII";
+ private SoftFont sf = new SoftFont();
+
+ private Thread reader = null;
+
+ private int charWidth;
+ private int charHeight;
+ private int charDescent;
+
+ private int termWidth;
+ private int termHeight;
+
+ private int color[] = {
+ Color.BLACK,
+ Color.RED,
+ Color.GREEN,
+ Color.YELLOW,
+ Color.BLUE,
+ Color.MAGENTA,
+ Color.CYAN,
+ Color.WHITE,
+ };
+
+ private final static int COLOR_FG_STD = 7;
+ private final static int COLOR_BG_STD = 0;
+
+ public JTATerminalView(Context context) {
+ super(context);
+
+ paint = new Paint();
+ paint.setAntiAlias(true);
+ setFont(Typeface.MONOSPACE, 8);
+
+ emulation = new vt320() {
+ public void write(byte[] b) {
+ try {
+ JTATerminalView.this.write(b);
+ } catch (IOException e) {
+ Log.e("SSH", "couldn't write" + b.toString());
+ reader = null;
+ }
+ }
+
+ public void sendTelnetCommand(byte cmd) {
+ // TODO: implement telnet command sending
+ }
+
+ public void setWindowSize(int c, int r) {
+ // TODO: implement window sizing
+ }
+ };
+
+ setVDUBuffer(emulation);
+ emulation.setDisplay(this);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (bitmap != null) {
+ canvas.drawBitmap(bitmap, 0, 0, null);
+ /*
+ if (charHeight > 0 && y > charHeight) {
+ // Invert pixels for cursor position.
+ Bitmap cursor = Bitmap.createBitmap(mBitmap, x, y - mCharHeight, mCharWidth, mCharHeight);
+ for (int cy = 0; cy < mCharHeight; cy++)
+ for (int cx = 0; cx < mCharWidth; cx++)
+ cursor.setPixel(cx, cy, (~cursor.getPixel(cx, cy) & 0xFFFFFFFF) | 0xFF000000);
+ canvas.drawBitmap(cursor, x, y - mCharHeight, null);
+ cursor = null;
+ }
+ */
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ Log.d("SSH/TerminalView", "onSizeChanged called");
+ Bitmap newBitmap = Bitmap.createBitmap(w, h, false);
+ Canvas newCanvas = new Canvas();
+
+ newCanvas.setDevice(newBitmap);
+
+ if (bitmap != null)
+ newCanvas.drawBitmap(bitmap, 0, 0, paint);
+
+ bitmap = newBitmap;
+ canvas = newCanvas;
+
+ setSize(w, h);
+ }
+
+ private void setSize(int w, int h) {
+ termWidth = w / charWidth;
+ termHeight = h / charHeight;
+
+ buffer.setScreenSize(termWidth, buffer.height = termHeight, true);
+ }
+
+ private void setFont(Typeface typeface, int size) {
+ paint.setTypeface(typeface);
+ paint.setTextSize(size);
+
+ FontMetricsInt fm = paint.getFontMetricsInt();
+
+ charDescent = fm.descent;
+
+ float[] widths = new float[1];
+ paint.getTextWidths("X", widths);
+ charWidth = (int)widths[0];
+
+ charHeight = Math.abs(fm.top) + Math.abs(fm.descent);
+ }
+
+ public void write(byte[] b) throws IOException {
+ Log.e("SSH/JTATerm/write", "Trying to write" + b.toString());
+ out.write(b);
+ }
+
+ public int getColumnCount() {
+ return termWidth;
+ }
+
+ public InputStream getInput() {
+ return in;
+ }
+
+ public byte[] getKeyCode(int keyCode, int meta) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_NEWLINE:
+ emulation.keyTyped(vt320.KEY_ENTER, ' ', meta);
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ emulation.keyPressed(vt320.KEY_LEFT, ' ', meta);
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ emulation.keyPressed(vt320.KEY_UP, ' ', meta);
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ emulation.keyPressed(vt320.KEY_DOWN, ' ', meta) ;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ emulation.keyPressed(vt320.KEY_RIGHT, ' ', meta);
+ break;
+ }
+ return null;
+ }
+
+ public OutputStream getOutput() {
+ return out;
+ }
+
+ public int getRowCount() {
+ return termHeight;
+ }
+
+ private int darken(int color) {
+ return Color.argb(0xFF,
+ (int)(Color.red(color) * 0.8),
+ (int)(Color.green(color) * 0.8),
+ (int)(Color.blue(color) * 0.8)
+ );
+ }
+
+ /*
+ private int brighten(int color) {
+ return Color.argb(0xFF,
+ (int)(Color.red(color) * 1.2),
+ (int)(Color.green(color) * 1.2),
+ (int)(Color.blue(color) * 1.2)
+ );
+ }
+ */
+
+ public void redraw() {
+ // Make sure the buffer is in the center of the screen.
+ int xoffset = (getWidth() - buffer.width * charWidth) / 2;
+ int yoffset = (getHeight() - buffer.height * charHeight) / 2;
+
+ // Draw the mouse-selection
+ //int selectStartLine = selectBegin.y - buffer.windowBase;
+ //int selectEndLine = selectEnd.y - buffer.windowBase;
+
+ int fg, bg;
+
+ for (int l = 0; l < buffer.height; l++) {
+ if (!buffer.update[0] && !buffer.update[l + 1]) continue;
+
+ for (int c = 0; c < buffer.width; c++) {
+ int addr = 0;
+ int currAttr = buffer.charAttributes[buffer.windowBase + l][c];
+
+ fg = darken(color[COLOR_FG_STD]);
+ bg = darken(color[COLOR_BG_STD]);
+
+ if ((currAttr & VDUBuffer.COLOR_FG) != 0)
+ fg = darken(color[((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1]);
+ if ((currAttr & VDUBuffer.COLOR_BG) != 0)
+ bg = darken(darken(color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1]));
+ paint.setFakeBoldText((currAttr & VDUBuffer.BOLD) != 0);
+
+ if ((currAttr & VDUBuffer.LOW) != 0)
+ fg = darken(fg);
+
+ if ((currAttr & VDUBuffer.INVERT) != 0) {
+ int swapc = bg;
+ bg = fg;
+ fg = swapc;
+ }
+
+ // If this character is in the special font, print it and continue to the next character.
+ if (sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c])) {
+ paint.setColor(bg);
+ canvas.drawRect(c * charWidth + xoffset, l * charHeight + yoffset,
+ c * (charWidth + 1) + xoffset, (l+1) * charHeight + yoffset, paint);
+ paint.setColor(fg);
+ paint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0);
+ if ((currAttr & VDUBuffer.INVISIBLE) == 0)
+ sf.drawChar(canvas, paint, buffer.charArray[buffer.windowBase + l][c], xoffset + c * charWidth, l * charHeight + yoffset, charWidth, charHeight);
+ continue;
+ }
+
+ // Determine the amount of continuous characters with the same settings and print them all at once.
+ while ((c + addr < buffer.width) &&
+ ((buffer.charArray[buffer.windowBase + l][c + addr] < ' ') ||
+ (buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr)) &&
+ !sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c + addr])) {
+ if (buffer.charArray[buffer.windowBase + l][c + addr] < ' ') {
+ buffer.charArray[buffer.windowBase + l][c + addr] = ' ';
+ buffer.charAttributes[buffer.windowBase + l][c + addr] = 0;
+ continue;
+ }
+ addr++;
+ }
+
+ paint.setColor(bg);
+ canvas.drawRect(c * charWidth + xoffset, l * charHeight + yoffset,
+ addr * (charWidth + 1) + xoffset, (l+1) * charHeight + yoffset, paint);
+ paint.setColor(fg);
+
+ paint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0);
+ if ((currAttr & VDUBuffer.INVISIBLE) == 0)
+ canvas.drawText(buffer.charArray[buffer.windowBase + l],
+ c, addr,
+ c * charWidth + xoffset,
+ (l + 1) * charHeight - charDescent + yoffset,
+ paint);
+
+ c += addr - 1;
+ }
+ }
+
+ buffer.update[0] = false;
+
+ postInvalidate();
+ }
+
+ public void updateScrollBar() {
+ // TODO Auto-generated method stub
+ }
+
+ public void start(InputStream in, OutputStream out) {
+ this.in = in;
+ this.out = out;
+
+ reader = new Thread(this);
+ reader.start();
+ }
+
+ public VDUBuffer getVDUBuffer() {
+ return buffer;
+ }
+
+ public void setVDUBuffer(VDUBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ public void run() {
+ byte[] b = new byte[256];
+ int n = 0;
+ while (n >= 0)
+ try {
+ n = in.read(b);
+ if (n > 0) emulation.putString(new String(b, 0, n, encoding));
+ redraw();
+ } catch (IOException e) {
+ reader = null;
+ break;
+ }
+ }
+}
diff --git a/src/org/theb/ssh/PasswordDialog.java b/src/org/theb/ssh/PasswordDialog.java
index 87839b7..faf636a 100644
--- a/src/org/theb/ssh/PasswordDialog.java
+++ b/src/org/theb/ssh/PasswordDialog.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import android.app.Activity;
diff --git a/src/org/theb/ssh/PreferencesDialog.java b/src/org/theb/ssh/PreferencesDialog.java
new file mode 100644
index 0000000..06bc229
--- /dev/null
+++ b/src/org/theb/ssh/PreferencesDialog.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+import android.app.Dialog;
+import android.content.Context;
+
+public class PreferencesDialog extends Dialog {
+
+ public PreferencesDialog(Context context) {
+ super(context);
+ // TODO Auto-generated constructor stub
+ }
+
+ public PreferencesDialog(Context context, int theme) {
+ super(context, theme);
+ // TODO Auto-generated constructor stub
+ }
+
+ public PreferencesDialog(Context context, boolean cancelable,
+ OnCancelListener cancelListener) {
+ super(context, cancelable, cancelListener);
+ // TODO Auto-generated constructor stub
+ }
+
+}
diff --git a/src/org/theb/ssh/SecureShell.java b/src/org/theb/ssh/SecureShell.java
index 31625f9..8d4dda6 100644
--- a/src/org/theb/ssh/SecureShell.java
+++ b/src/org/theb/ssh/SecureShell.java
@@ -1,12 +1,30 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
package org.theb.ssh;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
-import java.util.concurrent.Semaphore;
import org.theb.provider.HostDb;
+import com.trilead.ssh2.ConnectionMonitor;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -17,22 +35,16 @@ import android.os.Handler;
import android.text.method.KeyCharacterMap;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.View;
import android.view.Window;
-import android.widget.TextView;
-
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.ConnectionMonitor;
-import com.trilead.ssh2.Session;
-public class SecureShell extends Activity {
- private TextView mOutput;
+public class SecureShell extends Activity implements FeedbackUI, ConnectionMonitor {
private ConnectionThread mConn;
- private String mBuffer;
- private KeyCharacterMap mKMap;
+ // Activities we support.
static final int PASSWORD_REQUEST = 0;
+ // Database projection indices.
private static final int HOSTNAME_INDEX = 1;
private static final int USERNAME_INDEX = 2;
private static final int PORT_INDEX = 3;
@@ -46,6 +58,15 @@ public class SecureShell extends Activity {
private Cursor mCursor;
+ // Map to convert from keyboard presses to actual characters.
+ private KeyCharacterMap mKMap;
+
+ // Terminal window
+ private Terminal mTerminal;
+
+ // We change the meta state when the user presses the center button.
+ private boolean mMetaState = false;
+
// Store the username, hostname, and port from the database.
private String mHostname;
private String mUsername;
@@ -56,241 +77,21 @@ public class SecureShell extends Activity {
private boolean mIsWaiting;
private String mWaitingTitle;
private String mWaitingMessage;
-
- // Connection lost reason.
- private String mDisconnectReason;
-
- // This is for the password dialog.
- Semaphore sPass;
- String mPassword = null;
-
- Connection conn;
- Session sess;
- InputStream stdin;
- InputStream stderr;
- OutputStream stdout;
- int x;
- int y;
-
+
final Handler mHandler = new Handler();
- final Runnable mUpdateView = new Runnable() {
- public void run() {
- updateViewInUI();
- }
- };
-
- class ConnectionThread extends Thread {
- String hostname;
- String username;
- int port;
-
- char[][] lines;
- int posy = 0;
- int posx = 0;
-
- public ConnectionThread(String hostname, String username, int port) {
- this.hostname = hostname;
- this.username = username;
- this.port = port;
- }
-
- public void run() {
- conn = new Connection(hostname, port);
-
- conn.addConnectionMonitor(mConnectionMonitor);
-
- setWaiting(true, "Connection",
- "Connecting to " + hostname + "...");
-
- Log.d("SSH", "Starting connection attempt...");
-
- try {
- conn.connect(new InteractiveHostKeyVerifier());
-
- setWaiting(true, "Authenticating",
- "Trying to authenticate...");
-
- Log.d("SSH", "Starting authentication...");
-
-// boolean enableKeyboardInteractive = true;
-// boolean enableDSA = true;
-// boolean enableRSA = true;
-
- while (true) {
- /*
- if ((enableDSA || enableRSA ) &&
- mConn.isAuthMethodAvailable(username, "publickey");
- */
-
- if (conn.isAuthMethodAvailable(username, "password")) {
- Log.d("SSH", "Trying password authentication...");
- setWaiting(true, "Authenticating",
- "Trying to authenticate using password...");
-
- // Set a semaphore that is unset by the returning dialog.
- sPass = new Semaphore(0);
- askPassword();
-
- // Wait for the user to answer.
- sPass.acquire();
- sPass = null;
- if (mPassword == null)
- continue;
-
- boolean res = conn.authenticateWithPassword(username, mPassword);
- if (res == true)
- break;
-
- continue;
- }
-
- throw new IOException("No supported authentication methods available.");
- }
-
- Log.d("SSH", "Opening session...");
- setWaiting(true, "Session", "Requesting shell...");
-
- sess = conn.openSession();
-
- y = (int)(mOutput.getHeight() / mOutput.getLineHeight());
- // TODO: figure out how to get the width of monospace font characters.
- x = y * 3;
- Log.d("SSH", "Requesting PTY of size " + x + "x" + y);
-
- sess.requestPTY("dumb", x, y, 0, 0, null);
-
- Log.d("SSH", "Requesting shell...");
- sess.startShell();
-
- stdout = sess.getStdin();
- stderr = sess.getStderr();
- stdin = sess.getStdout();
-
- setWaiting(false, null, null);
- } catch (IOException e) {
- Log.e("SSH", e.getMessage());
- setWaiting(false, null, null);
- return;
- } catch (InterruptedException e) {
- // This thread is coming to an end. Let us exit.
- Log.e("SSH", "Connection thread interrupted.");
- return;
- }
-
- byte[] buff = new byte[8192];
- lines = new char[y][];
-
- try {
- while (true) {
- if ((stdin.available() == 0) && (stderr.available() == 0)) {
- int conditions = sess.waitForCondition(
- ChannelCondition.STDOUT_DATA
- | ChannelCondition.STDERR_DATA
- | ChannelCondition.EOF, 2000);
- if ((conditions & ChannelCondition.TIMEOUT) != 0)
- continue;
- if ((conditions & ChannelCondition.EOF) != 0)
- if ((conditions &
- (ChannelCondition.STDERR_DATA
- | ChannelCondition.STDOUT_DATA)) == 0)
- break;
- }
-
- if (stdin.available() > 0) {
- int len = stdin.read(buff);
- addText(buff, len);
- }
- if (stderr.available() > 0) {
- int len = stderr.read(buff);
- addText(buff, len);
- }
- }
- } catch (Exception e) {
- Log.e("SSH", "Got exception reading: " + e.getMessage());
- }
- }
-
- public void addText(byte[] data, int len) {
- for (int i = 0; i < len; i++) {
- char c = (char) (data[i] & 0xff);
-
- if (c == 8) { // Backspace, VERASE
- if (posx < 0)
- continue;
- posx--;
- continue;
- }
- if (c == '\r') {
- posx = 0;
- continue;
- }
-
- if (c == '\n') {
- posy++;
- if (posy >= y) {
- for (int k = 1; k < y; k++)
- lines[k - 1] = lines[k];
-
- posy--;
- lines[y - 1] = new char[x];
-
- for (int k = 0; k < x; k++)
- lines[y - 1][k] = ' ';
- }
- continue;
- }
-
- if (c < 32) {
- continue;
- }
-
- if (posx >= x) {
- posx = 0;
- posy++;
- if (posy >= y) {
- posy--;
-
- for (int k = 1; k < y; k++)
- lines[k - 1] = lines[k];
- lines[y - 1] = new char[x];
- for (int k = 0; k < x; k++)
- lines[y - 1][k] = ' ';
- }
- }
-
- if (lines[posy] == null) {
- lines[posy] = new char[x];
- for (int k = 0; k < x; k++)
- lines[posy][k] = ' ';
- }
+ // Tell the user why we disconnected.
+ private String mDisconnectReason;
- lines[posy][posx] = c;
- posx++;
- }
-
- StringBuffer sb = new StringBuffer(x * y);
-
- for (int i = 0; i < lines.length; i++) {
- if (i != 0)
- sb.append('\n');
-
- if (lines[i] != null)
- sb.append(lines[i]);
- }
-
- mBuffer = sb.toString();
- mHandler.post(mUpdateView);
- }
- }
-
@Override
public void onCreate(Bundle savedValues) {
super.onCreate(savedValues);
requestWindowFeature(Window.FEATURE_PROGRESS);
- setContentView(R.layout.secure_shell);
- mOutput = (TextView) findViewById(R.id.output);
+ mTerminal = new JTATerminalView(this);
+
+ // TODO: implement scroll bar on right.
+ setContentView((View)mTerminal);
Log.d("SSH", "using URI " + getIntent().getData().toString());
@@ -307,7 +108,7 @@ public class SecureShell extends Activity {
this.setTitle(title);
- mConn = new ConnectionThread(mHostname, mUsername, mPort);
+ mConn = new TrileadConnectionThread(this, mTerminal, mHostname, mUsername, mPort);
mKMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
@@ -340,10 +141,9 @@ public class SecureShell extends Activity {
}
};
- public String askPassword() {
+ public void askPassword() {
Intent intent = new Intent(this, PasswordDialog.class);
this.startSubActivity(intent, PASSWORD_REQUEST);
- return null;
}
@Override
@@ -351,55 +151,71 @@ public class SecureShell extends Activity {
String data, Bundle extras)
{
if (requestCode == PASSWORD_REQUEST) {
-
- // If the request was cancelled, then we didn't get anything.
- if (resultCode == RESULT_CANCELED)
- mPassword = "";
- else
- mPassword = data;
-
- sPass.release();
+ mConn.setPassword(data);
}
}
@Override
public void onDestroy() {
super.onDestroy();
-
- if (sess != null) {
- sess.close();
- sess = null;
- }
-
- if (conn != null) {
- conn.close();
- conn = null;
- }
+
+ mConn.finish();
+ mConn = null;
finish();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
- if (stdout != null) {
- int c = mKMap.get(keyCode, msg.getMetaState());
+ final OutputStream out = mConn.getWriter();
+ if (out != null) {
try {
- stdout.write(c);
+ if (mKMap.isPrintingKey(keyCode)
+ || keyCode == KeyEvent.KEYCODE_SPACE) {
+ int c = mKMap.get(keyCode, msg.getMetaState());
+ if (mMetaState) {
+ // Support CTRL-A through CTRL-Z
+ if (c >= 0x61 && c <= 0x79)
+ c -= 0x60;
+ else if (c >= 0x40 && c <= 0x59)
+ c -= 0x39;
+ mMetaState = false;
+ }
+ out.write(c);
+ } else if (keyCode == KeyEvent.KEYCODE_DEL) {
+ out.write(0x08); // CTRL-H
+ } else if (keyCode == KeyEvent.KEYCODE_NEWLINE
+ || keyCode == KeyEvent.KEYCODE_DPAD_LEFT
+ || keyCode == KeyEvent.KEYCODE_DPAD_UP
+ || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
+ || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ byte[] output = mTerminal.getKeyCode(keyCode, msg.getMetaState());
+ if (output != null)
+ out.write(output);
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ if (mMetaState) {
+ out.write(0x1B); // ESCAPE
+ mMetaState = false;
+ } else {
+ mMetaState = true;
+ }
+ } else {
+ // This is not something we handle.
+ return super.onKeyDown(keyCode, msg);
+ }
+ return true;
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Log.e("SSH", "Can't write to stdout: "+ e.getMessage());
}
}
-
- return super.onKeyDown(keyCode, msg);
- }
-
- public void updateViewInUI() {
- mOutput.setText(mBuffer);
+ return super.onKeyDown(keyCode, msg);
}
final Runnable mDisconnectAlert = new Runnable() {
public void run() {
+ if (SecureShell.this.isFinishing())
+ return;
+
AlertDialog d = AlertDialog.show(SecureShell.this,
"Connection Lost", mDisconnectReason, "Ok", false);
d.show();
@@ -407,11 +223,9 @@ public class SecureShell extends Activity {
}
};
- final ConnectionMonitor mConnectionMonitor = new ConnectionMonitor() {
- public void connectionLost(Throwable reason) {
- Log.d("SSH", "Connection ended.");
- mDisconnectReason = reason.getMessage();
- mHandler.post(mDisconnectAlert);
- }
- };
+ public void connectionLost(Throwable reason) {
+ Log.d("SSH", "Connection ended.");
+ mDisconnectReason = reason.getMessage();
+ mHandler.post(mDisconnectAlert);
+ }
}
diff --git a/src/org/theb/ssh/ShellView.java b/src/org/theb/ssh/ShellView.java
deleted file mode 100644
index 15d783b..0000000
--- a/src/org/theb/ssh/ShellView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.theb.ssh;
-
-import java.io.IOException;
-
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.Session;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.Log;
-import android.widget.EditText;
-import android.widget.TextView;
-
-public class ShellView extends EditText {
-
- private Connection mConn;
- private Session mSess;
-
- private String mHostname;
- private String mUsername;
- private String mPassword;
-
- public ShellView(Context context) {
- super(context);
- // TODO Auto-generated constructor stub
-
- mPassword = "OEfmP07-";
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- append("Connecting... ");
- mConn = new Connection(mHostname);
-
- try {
- mConn.connect(new InteractiveHostKeyVerifier());
- append("OK\n");
- } catch (IOException e) {
- append("Failed.\n");
- Log.e("SSH", e.getMessage());
- append("\nWhoops: " + e.getCause().getMessage() + "\n");
- }
-
- boolean enableKeyboardInteractive = true;
- boolean enableDSA = true;
- boolean enableRSA = true;
-
- try {
- while (true) {
- /*
- if ((enableDSA || enableRSA ) &&
- mConn.isAuthMethodAvailable(username, "publickey");
- */
-
- if (mConn.isAuthMethodAvailable(mUsername, "password")) {
- boolean res = mConn.authenticateWithPassword(mUsername, mPassword);
- if (res == true)
- break;
-
- append("Login failed.\n");
- continue;
- }
-
- throw new IOException("No supported authentication methods available.");
- }
-
- mSess = mConn.openSession();
- append("Logged in as " + mUsername + ".\n");
- } catch (IOException e) {
- Log.e("SSH", e.getMessage());
- }
- append("Exiting\n");
- }
-
-}
diff --git a/src/org/theb/ssh/Terminal.java b/src/org/theb/ssh/Terminal.java
new file mode 100644
index 0000000..1212aa2
--- /dev/null
+++ b/src/org/theb/ssh/Terminal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public interface Terminal {
+ public int getWidth();
+ public int getHeight();
+ public int getRowCount();
+ public int getColumnCount();
+ public void start(InputStream in, OutputStream out);
+ public byte[] getKeyCode(int keyCode, int meta);
+}
diff --git a/src/org/theb/ssh/TrileadConnectionThread.java b/src/org/theb/ssh/TrileadConnectionThread.java
new file mode 100644
index 0000000..63180b0
--- /dev/null
+++ b/src/org/theb/ssh/TrileadConnectionThread.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Kenny Root (kenny at the-b.org)
+ *
+ * This file is part of Connectbot.
+ *
+ * Connectbot 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.
+ *
+ * Connectbot 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 Connectbot. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.theb.ssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.Semaphore;
+
+import com.trilead.ssh2.Connection;
+import com.trilead.ssh2.ConnectionMonitor;
+import com.trilead.ssh2.Session;
+
+public class TrileadConnectionThread extends ConnectionThread {
+ private String hostname;
+ private String username;
+ private String password;
+ private int port;
+
+ private Connection connection;
+ private Session session;
+
+ private InputStream stdOut;
+ private OutputStream stdIn;
+
+ private Semaphore sPass;
+
+ protected FeedbackUI ui;
+ protected Terminal term;
+
+ public TrileadConnectionThread(FeedbackUI ui, Terminal term, String hostname, String username, int port) {
+ super(ui, hostname, username, port);
+ this.ui = ui;
+ this.term = term;
+ this.hostname = hostname;
+ this.username = username;
+ this.port = port;
+ }
+
+ @Override
+ public void finish() {
+ if (session != null) {
+ session.close();
+ session = null;
+ }
+
+ if (connection != null) {
+ connection.close();
+ connection = null;
+ }
+ }
+
+ @Override
+ public InputStream getReader() {
+ return stdOut;
+ }
+
+ @Override
+ public OutputStream getWriter() {
+ return stdIn;
+ }
+
+ @Override
+ public void run() {
+ connection = new Connection(hostname, port);
+
+ connection.addConnectionMonitor((ConnectionMonitor) ui);
+
+ ui.setWaiting(true, "Connection", "Connecting to " + hostname + "...");
+
+ try {
+ connection.connect(new InteractiveHostKeyVerifier());
+
+ ui.setWaiting(true, "Authenticating", "Trying to authenticate...");
+
+ // boolean enableKeyboardInteractive = true;
+ // boolean enableDSA = true;
+ // boolean enableRSA = true;
+
+ while (true) {
+ /*
+ * if ((enableDSA || enableRSA ) &&
+ * mConn.isAuthMethodAvailable(username, "publickey");
+ */
+
+ if (connection.isAuthMethodAvailable(username, "password")) {
+ ui.setWaiting(true, "Authenticating",
+ "Trying to authenticate using password...");
+
+ // Set a semaphore that is unset by the returning dialog.
+ sPass = new Semaphore(0);
+ ui.askPassword();
+
+ // Wait for the user to answer.
+ sPass.acquire();
+ sPass = null;
+ if (password == null)
+ continue;
+
+ boolean res = connection.authenticateWithPassword(username,
+ password);
+ password = null;
+ if (res == true)
+ break;
+
+ continue;
+ }
+
+ throw new IOException(
+ "No supported authentication methods available.");
+ }
+
+ ui.setWaiting(true, "Session", "Requesting shell...");
+
+ session = connection.openSession();
+
+ session.requestPTY("vt100",
+ term.getColumnCount(), term.getRowCount(),
+ term.getWidth(), term.getHeight(),
+ null);
+
+ session.startShell();
+
+ stdIn = session.getStdin();
+ // stderr = session.getStderr();
+ stdOut = session.getStdout();
+
+ ui.setWaiting(false, null, null);
+ } catch (IOException e) {
+ ui.setWaiting(false, null, null);
+ return;
+ } catch (InterruptedException e) {
+ // This thread is coming to an end. Let us exit.
+ return;
+ }
+
+ term.start(stdOut, stdIn);
+ }
+
+ @Override
+ public void setPassword(String password) {
+ if (password == null)
+ this.password = "";
+ else
+ this.password = password;
+ sPass.release();
+ }
+}