Skip to content

Commit

Permalink
VNCConn,VncCanvas: move direct VncCanvas calls from VNCConn to interf…
Browse files Browse the repository at this point in the history
…aces that VncCanvas implements

re #126

Also fixes:

* SSH key mismatch not being HTML'ed.
* SSH key updating on mismatch override logic being wrong.
  • Loading branch information
bk138 committed Aug 15, 2023
1 parent 463ff82 commit 2737d6c
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,36 @@
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.Keep;

import com.coboltforge.dontmind.multivnc.db.ConnectionBean;
import com.coboltforge.dontmind.multivnc.db.SshKnownHost;
import com.coboltforge.dontmind.multivnc.db.VncDatabase;
import com.coboltforge.dontmind.multivnc.ui.VncCanvas;


public class VNCConn {

public interface OnFramebufferEventListener {
void onFramebufferUpdateFinished();
void onNewFramebufferSize(int w, int h);
}

public interface OnAuthEventListener {
void onRequestCredsFromUser(final ConnectionBean conn, boolean isUserNameNeeded);
void onRequestSshFingerprintCheck(String host, byte[] fingerprint, final AtomicBoolean doContinue);
}

private final OnFramebufferEventListener onFramebufferEventCallback;
private final OnAuthEventListener onAuthEventCallback;

static {
System.loadLibrary("vncconn");
}
Expand Down Expand Up @@ -426,7 +436,9 @@ private native boolean rfbInit(String host, int port, int repeaterId, int bytesP
private native boolean rfbIsEncrypted();


public VNCConn() {
public VNCConn(OnFramebufferEventListener framebufferCallback, OnAuthEventListener authCallback) {
onFramebufferEventCallback = framebufferCallback;
onAuthEventCallback = authCallback;
if(Utils.DEBUG()) Log.d(TAG, this + " constructed!");
}

Expand Down Expand Up @@ -736,20 +748,8 @@ public final ConnectionBean getConnSettings() {
// called from native via worker thread context
@Keep
private void onFramebufferUpdateFinished() {

canvas.reDraw();

// Hide progress dialog
if (canvas.activity.firstFrameWaitDialog.isShowing())
canvas.handler.post(new Runnable() {
public void run() {
try {
canvas.activity.firstFrameWaitDialog.dismiss();
} catch (Exception e){
//unused
}
}
});
if(onFramebufferEventCallback != null)
onFramebufferEventCallback.onFramebufferUpdateFinished();
}

// called from native via worker thread context
Expand All @@ -762,8 +762,8 @@ private void onGotCutText(byte[] bytes) {
// called from native via worker thread context
@Keep
private String onGetPassword() {
if (connSettings.password == null || connSettings.password.length() == 0) {
canvas.getCredsFromUser(connSettings, false); // this cares for running on the main thread
if (onAuthEventCallback != null && (connSettings.password == null || connSettings.password.length() == 0)) {
onAuthEventCallback.onRequestCredsFromUser(connSettings, false); // this cares for running on the main thread
synchronized (VNCConn.this) {
try {
VNCConn.this.wait(); // wait for user input to finish
Expand All @@ -788,9 +788,10 @@ private static class UserCredential {
@Keep
private UserCredential onGetUserCredential() {

if (connSettings.userName == null || connSettings.userName.isEmpty()
|| connSettings.password == null || connSettings.password.isEmpty()) {
canvas.getCredsFromUser(connSettings, connSettings.userName == null || connSettings.userName.isEmpty());
if (onAuthEventCallback != null &&
(connSettings.userName == null || connSettings.userName.isEmpty()
|| connSettings.password == null || connSettings.password.isEmpty())) {
onAuthEventCallback.onRequestCredsFromUser(connSettings, connSettings.userName == null || connSettings.userName.isEmpty());
synchronized (VNCConn.this) {
try {
VNCConn.this.wait(); // wait for user input to finish
Expand All @@ -810,61 +811,29 @@ private UserCredential onGetUserCredential() {
@Keep
private void onNewFramebufferSize(int w, int h) {
Log.d(TAG, "new framebuffer size " + w + " x " + h);
// this triggers an update on what the canvas thinks about cursor position.
// without this, the pointer highlight is off by some value after framebuffer size change
canvas.handler.post(() -> {
canvas.pan(0, 0);
});
if(onFramebufferEventCallback != null)
onFramebufferEventCallback.onNewFramebufferSize(w, h);
}

// called from native via worker thread context
@Keep
private int onSshFingerprintCheck(String host, byte[] fingerprint) {
// look for host, if not found create entry and return ok
SshKnownHost knownHost = VncDatabase.getInstance(canvas.getContext()).getSshKnownHostDao().get(host);
if(knownHost == null) {
if(onAuthEventCallback != null) {
AtomicBoolean doContinue = new AtomicBoolean();
// always using SHA256 in native part, ok to hardocde this here
canvas.getSshFingerPrintNewDecision("SHA256:" + Base64.encodeToString(fingerprint,Base64.NO_PADDING|Base64.NO_WRAP), doContinue); // this cares for running on the main thread
onAuthEventCallback.onRequestSshFingerprintCheck(host, fingerprint, doContinue);
synchronized (VNCConn.this) {
try {
VNCConn.this.wait(); // wait for user input to finish
} catch (InterruptedException e) {
//unused
}
}
// evaluate result
if(doContinue.get()) {
VncDatabase.getInstance(canvas.getContext()).getSshKnownHostDao().insert(new SshKnownHost(0, host, fingerprint));
return 0;
}
else {
return -1;
}
}

// host found, check if fingerprint matches
if(Arrays.equals(knownHost.fingerprint, fingerprint))
return 0;
else {
// not matching, ask user!
AtomicBoolean doContinue = new AtomicBoolean();
// always using SHA256 in native part, ok to hardocde this here
canvas.getSshFingerPrintMismatchDecision("SHA256:" + Base64.encodeToString(fingerprint,Base64.NO_PADDING|Base64.NO_WRAP), doContinue); // this cares for running on the main thread
synchronized (VNCConn.this) {
try {
VNCConn.this.wait(); // wait for user input to finish
} catch (InterruptedException e) {
//unused
}
}
if(doContinue.get())
return 0;
else {
SshKnownHost updatedHost = new SshKnownHost(knownHost.id, host, fingerprint);
VncDatabase.getInstance(canvas.getContext()).getSshKnownHostDao().update(updatedHost);
return -1;
}
}
return -1;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

package com.coboltforge.dontmind.multivnc.ui;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.microedition.khronos.egl.EGLConfig;
Expand All @@ -44,6 +45,7 @@
import android.os.Handler;
import android.text.Html;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
Expand All @@ -60,9 +62,11 @@
import com.coboltforge.dontmind.multivnc.VNCConn;
import com.coboltforge.dontmind.multivnc.db.ConnectionBean;
import com.coboltforge.dontmind.multivnc.db.MetaKeyBean;
import com.coboltforge.dontmind.multivnc.db.SshKnownHost;
import com.coboltforge.dontmind.multivnc.db.VncDatabase;


public class VncCanvas extends GLSurfaceView {
public class VncCanvas extends GLSurfaceView implements VNCConn.OnFramebufferEventListener, VNCConn.OnAuthEventListener {
static {
System.loadLibrary("vnccanvas");
}
Expand Down Expand Up @@ -836,7 +840,30 @@ public int getVisibleHeight() {
return (int)((double)getHeight() / getScale() + 0.5);
}

public void getCredsFromUser(final ConnectionBean c, boolean isUserNameNeeded) {
@Override
public void onFramebufferUpdateFinished() {
reDraw();

// Hide progress dialog
if (activity.firstFrameWaitDialog.isShowing())
handler.post(() -> {
try {
activity.firstFrameWaitDialog.dismiss();
} catch (Exception e){
//unused
}
});
}

@Override
public void onNewFramebufferSize(int w, int h) {
// this triggers an update on what the canvas thinks about cursor position.
// without this, the pointer highlight is off by some value after framebuffer size change
handler.post(() -> pan(0, 0));
}

@Override
public void onRequestCredsFromUser(final ConnectionBean c, boolean isUserNameNeeded) {
// this method is probably called from the vnc thread
post(new Runnable() {
@Override
Expand Down Expand Up @@ -869,55 +896,72 @@ public void onClick(DialogInterface dialog, int whichButton) {

}

public void getSshFingerPrintNewDecision(String fingerprint, final AtomicBoolean doContinue) {
@Override
public void onRequestSshFingerprintCheck(String host, byte[] fingerprint, AtomicBoolean doContinue) {
// this method is probably called from the vnc thread
post(() -> {
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.ssh_key_new_title)
.setMessage(Html.fromHtml(getContext().getString(R.string.ssh_key_new_message, fingerprint)))
.setCancelable(false)
.setPositiveButton(R.string.ssh_key_new_continue, (dialog12, whichButton) -> {
doContinue.set(true);
synchronized (vncConn) {
vncConn.notify();
}
})
.setNegativeButton(R.string.ssh_key_new_abort, (dialog1, whichButton) -> {
doContinue.set(false);
synchronized (vncConn) {
vncConn.notify();
}
})
.create();

dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
});
}
// look for host, if not found create entry and return ok
SshKnownHost knownHost = VncDatabase.getInstance(getContext()).getSshKnownHostDao().get(host);
if(knownHost == null) {
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.ssh_key_new_title)
// always using SHA256 in native part, ok to hardcode this here
.setMessage(Html.fromHtml(getContext().getString(R.string.ssh_key_new_message,
"SHA256:" + Base64.encodeToString(fingerprint,Base64.NO_PADDING|Base64.NO_WRAP))))
.setCancelable(false)
.setPositiveButton(R.string.ssh_key_new_continue, (dialog12, whichButton) -> {
VncDatabase.getInstance(getContext()).getSshKnownHostDao().insert(new SshKnownHost(0, host, fingerprint));
doContinue.set(true);
synchronized (vncConn) {
vncConn.notify();
}
})
.setNegativeButton(R.string.ssh_key_new_abort, (dialog1, whichButton) -> {
doContinue.set(false);
synchronized (vncConn) {
vncConn.notify();
}
})
.create();

public void getSshFingerPrintMismatchDecision(String fingerprint, final AtomicBoolean doContinue) {
// this method is probably called from the vnc thread
post(() -> {
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.ssh_key_mismatch_title)
.setMessage(getContext().getString(R.string.ssh_key_mismatch_message, fingerprint))
.setCancelable(false)
.setPositiveButton(R.string.ssh_key_mismatch_continue, (dialog12, whichButton) -> {
doContinue.set(true);
synchronized (vncConn) {
vncConn.notify();
}
})
.setNegativeButton(R.string.ssh_key_mismatch_abort, (dialog1, whichButton) -> {
doContinue.set(false);
synchronized (vncConn) {
vncConn.notify();
}
})
.create();

dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
return;
}

// host found, check if fingerprint matches
if(Arrays.equals(knownHost.fingerprint, fingerprint)) {
doContinue.set(true);
synchronized (vncConn) {
vncConn.notify();
}
} else {
// not matching, ask user!
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.ssh_key_mismatch_title)
// always using SHA256 in native part, ok to hardcode this here
.setMessage(Html.fromHtml(getContext().getString(R.string.ssh_key_mismatch_message,
"SHA256:" + Base64.encodeToString(fingerprint,Base64.NO_PADDING|Base64.NO_WRAP))))
.setCancelable(false)
.setPositiveButton(R.string.ssh_key_mismatch_continue, (dialog12, whichButton) -> {
SshKnownHost updatedHost = new SshKnownHost(knownHost.id, host, fingerprint);
VncDatabase.getInstance(getContext()).getSshKnownHostDao().update(updatedHost);
doContinue.set(true);
synchronized (vncConn) {
vncConn.notify();
}
})
.setNegativeButton(R.string.ssh_key_mismatch_abort, (dialog1, whichButton) -> {
doContinue.set(false);
synchronized (vncConn) {
vncConn.notify();
}
})
.create();

dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public void onSystemUiVisibilityChange(int visibility) {
/*
* Setup canvas and conn.
*/
VNCConn conn = new VNCConn();
VNCConn conn = new VNCConn(vncCanvas, vncCanvas);
vncCanvas.initializeVncCanvas(this, inputHandler, conn); // add conn to canvas
conn.setCanvas(vncCanvas); // add canvas to conn. be sure to call this before init!
// the actual connection init
Expand Down

0 comments on commit 2737d6c

Please sign in to comment.