Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
7f840d25d9 Add a simple Tor relay probe. 2018-10-24 10:38:49 +01:00
50 changed files with 371 additions and 504 deletions

View File

@@ -9,8 +9,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10104
versionName "1.1.4"
versionCode 10103
versionName "1.1.3"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@@ -16,27 +16,18 @@ import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
@@ -46,13 +37,8 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -61,11 +47,8 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG =
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
@@ -75,13 +58,12 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Clock clock,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.clock = clock;
}
@Override
@@ -200,74 +182,6 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
}
}
@Override
@Nullable
DuplexTransportConnection discoverAndConnect(String uuid) {
if (adapter == null) return null;
for (String address : discoverDevices()) {
try {
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
return connectTo(address, uuid);
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to "
+ scrubMacAddress(address));
}
}
}
LOG.info("Could not connect to any devices");
return null;
}
private Collection<String> discoverDevices() {
List<String> addresses = new ArrayList<>();
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
DiscoveryReceiver receiver = new DiscoveryReceiver(intents);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND);
appContext.registerReceiver(receiver, filter);
try {
if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis();
long end = now + MAX_DISCOVERY_MS;
while (now < end) {
Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break;
String action = i.getAction();
if (ACTION_DISCOVERY_STARTED.equals(action)) {
LOG.info("Discovery started");
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
LOG.info("Discovery finished");
break;
} else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
String address = d.getAddress();
if (LOG.isLoggable(INFO))
LOG.info("Discovered " + scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
now = clock.currentTimeMillis();
}
} else {
LOG.info("Could not start discovery");
}
} catch (InterruptedException e) {
LOG.info("Interrupted while discovering devices");
Thread.currentThread().interrupt();
} finally {
LOG.info("Cancelling discovery");
adapter.cancelDiscovery();
appContext.unregisterReceiver(receiver);
}
// Shuffle the addresses so we don't always try the same one first
Collections.shuffle(addresses);
return addresses;
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
@@ -293,18 +207,4 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
}
}
}
private static class DiscoveryReceiver extends BroadcastReceiver {
private final BlockingQueue<Intent> intents;
private DiscoveryReceiver(BlockingQueue<Intent> intents) {
this.intents = intents;
}
@Override
public void onReceive(Context ctx, Intent intent) {
intents.add(intent);
}
}
}

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
@@ -34,19 +33,17 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final Context appContext;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final Clock clock;
private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, Clock clock,
SecureRandom secureRandom, EventBus eventBus,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.clock = clock;
this.backoffFactory = backoffFactory;
}
@@ -68,7 +65,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, clock, backoff, callback, MAX_LATENCY);
secureRandom, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -0,0 +1,153 @@
package org.briarproject.bramble.plugin.tor;
import org.spongycastle.crypto.tls.Certificate;
import org.spongycastle.crypto.tls.CipherSuite;
import org.spongycastle.crypto.tls.DefaultTlsClient;
import org.spongycastle.crypto.tls.ServerOnlyTlsAuthentication;
import org.spongycastle.crypto.tls.TlsAuthentication;
import org.spongycastle.crypto.tls.TlsClientProtocol;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
public class TorProbe {
private static final Logger LOG =
Logger.getLogger(TorProbe.class.getName());
private static final int READ_TIMEOUT = 10 * 1000;
// https://trac.torproject.org/projects/tor/wiki/org/projects/Tor/TLSHistory
private static final int SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xfeff;
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n347
private static final int[] TOR_CIPHER_SUITES = new int[] {
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
CipherSuite.TLS_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
};
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n412
private static final byte[] VERSIONS_CELL = new byte[] {
0x00, 0x00, // Circuit ID: 0
0x07, // Command: Versions
0x00, 0x06, // Payload length: 6 bytes
0x00, 0x03, 0x00, 0x04, 0x00, 0x05 // Supported versions: 3, 4, 5
};
public List<Integer> probe(String address, int port) throws IOException {
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + address + ":" + port);
Socket socket = new Socket(address, port);
LOG.info("Connected");
TlsClientProtocol client = new TlsClientProtocol(
socket.getInputStream(), socket.getOutputStream(),
new SecureRandom());
client.connect(new TorTlsClient());
LOG.info("TLS handshake succeeded");
socket.setSoTimeout(READ_TIMEOUT);
try {
// Send a versions cell
OutputStream out = client.getOutputStream();
out.write(VERSIONS_CELL);
out.flush();
LOG.info("Sent versions cell");
// Expect a versions cell in response
List<Integer> versions = new ArrayList<>();
DataInputStream in = new DataInputStream(client.getInputStream());
int circuitId = in.readUnsignedShort();
if (circuitId != 0)
throw new IOException("Unexpected circuit ID: " + circuitId);
int command = in.readUnsignedByte();
if (command != 7)
throw new IOException("Unexpected command: " + command);
int payloadLength = in.readUnsignedShort();
if (payloadLength == 0 || payloadLength % 2 != 0) {
throw new IOException("Invalid payload length: "
+ payloadLength);
}
for (int i = 0; i < payloadLength / 2; i++) {
int version = in.readUnsignedShort();
versions.add(version);
}
if (LOG.isLoggable(INFO))
LOG.info("Supported versions: " + versions);
return versions;
} finally {
client.close();
}
}
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Usage: TorProbe <address> <port>");
System.exit(1);
}
String address = args[0];
int port = Integer.parseInt(args[1]);
new TorProbe().probe(address, port);
}
private static class TorTlsClient extends DefaultTlsClient {
@Override
public TlsAuthentication getAuthentication() {
return new ServerOnlyTlsAuthentication() {
@Override
public void notifyServerCertificate(Certificate cert)
throws IOException {
LOG.info("Received server certificate");
org.spongycastle.asn1.x509.Certificate[] chain =
cert.getCertificateList();
if (chain.length != 1)
throw new IOException("Certificate is not self-signed");
for (org.spongycastle.asn1.x509.Certificate c : chain) {
if (LOG.isLoggable(INFO)) {
LOG.info("Subject: " + c.getSubject());
LOG.info("Issuer: " + c.getIssuer());
}
}
}
};
}
@Override
public int[] getCipherSuites() {
return TOR_CIPHER_SUITES;
}
}
}

View File

@@ -21,7 +21,7 @@ public interface KeyAgreementConstants {
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 60_000;
long CONNECTION_TIMEOUT = 20 * 1000;
/**
* The transport identifier for Bluetooth.

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
import java.security.SecureRandom;
@@ -45,9 +46,6 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.bramble.util.StringUtils.macToBytes;
import static org.briarproject.bramble.util.StringUtils.macToString;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -98,9 +96,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException;
@Nullable
abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
@@ -198,7 +193,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
address = getBluetoothAddress();
if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address));
if (!isNullOrEmpty(address)) {
if (!StringUtils.isNullOrEmpty(address)) {
p.put(PROP_ADDRESS, address);
changed = true;
}
@@ -261,9 +256,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
// Try to connect to known devices in parallel
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
String address = e.getValue().get(PROP_ADDRESS);
if (isNullOrEmpty(address)) continue;
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (isNullOrEmpty(uuid)) continue;
if (StringUtils.isNullOrEmpty(uuid)) continue;
ContactId c = e.getKey();
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
@@ -314,9 +309,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS);
if (isNullOrEmpty(address)) return null;
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return null;
if (StringUtils.isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
@@ -331,6 +326,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!isRunning()) return null;
// There's no point listening if we can't discover our own address
String address = getBluetoothAddress();
if (address == null) return null;
// No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
@@ -348,8 +346,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
}
BdfList descriptor = new BdfList();
descriptor.add(TRANSPORT_ID_BLUETOOTH);
String address = getBluetoothAddress();
if (address != null) descriptor.add(macToBytes(address));
descriptor.add(StringUtils.macToBytes(address));
return new BluetoothKeyAgreementListener(descriptor, ss);
}
@@ -357,25 +354,18 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
DuplexTransportConnection conn;
if (descriptor.size() == 1) {
if (LOG.isLoggable(INFO))
LOG.info("Discovering address for key agreement UUID " + uuid);
conn = discoverAndConnect(uuid);
} else {
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid);
}
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
return conn;
}
@@ -383,7 +373,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return macToString(mac);
return StringUtils.macToString(mac);
}
@Override

View File

@@ -108,12 +108,6 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return wrapSocket((StreamConnection) Connector.open(url));
}
@Override
@Nullable
DuplexTransportConnection discoverAndConnect(String uuid) {
return null; // TODO
}
private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
}

View File

@@ -22,8 +22,8 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 10104
versionName "1.1.4"
versionCode 10103
versionName "1.1.3"
applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""

View File

@@ -7,7 +7,6 @@
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />

View File

@@ -107,7 +107,7 @@ public class AppModule {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, clock, backoffFactory);
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider,

View File

@@ -34,7 +34,6 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -43,8 +42,6 @@ import static android.arch.lifecycle.Lifecycle.State.STARTED;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
/**
@@ -54,8 +51,6 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT
public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext, OnTapFilteredListener {
private final static Logger LOG = getLogger(BaseActivity.class.getName());
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
@@ -124,8 +119,6 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onStart() {
super.onStart();
if (LOG.isLoggable(INFO))
LOG.info("Starting " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStart();
}
@@ -144,8 +137,6 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onStop() {
super.onStop();
if (LOG.isLoggable(INFO))
LOG.info("Stopping " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStop();
}

View File

@@ -9,9 +9,9 @@ public interface RequestCodes {
int REQUEST_WRITE_BLOG_POST = 5;
int REQUEST_SHARE_BLOG = 6;
int REQUEST_RINGTONE = 7;
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
int REQUEST_PERMISSION_CAMERA = 8;
int REQUEST_DOZE_WHITELISTING = 9;
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
int REQUEST_ENABLE_BLUETOOTH = 10;
int REQUEST_UNLOCK = 11;
int REQUEST_KEYGUARD_UNLOCK = 12;

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.keyagreement;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
@@ -10,15 +11,19 @@ import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
import org.briarproject.briar.R;
import org.briarproject.briar.R.string;
import org.briarproject.briar.R.style;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment;
@@ -32,19 +37,16 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.CAMERA;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
import static android.os.Build.VERSION.SDK_INT;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -53,11 +55,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
KeyAgreementEventListener {
private enum BluetoothState {
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE
}
private enum Permission {
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED
}
private static final Logger LOG =
@@ -66,27 +64,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Inject
EventBus eventBus;
/**
* Set to true in onPostResume() and false in onPause(). This prevents the
* QR code fragment from being shown if onRequestPermissionsResult() is
* called while the activity is paused, which could cause a crash due to
* https://issuetracker.google.com/issues/37067655.
*/
private boolean isResumed = false;
/**
* Set to true when the continue button is clicked, and false when the QR
* code fragment is shown. This prevents the QR code fragment from being
* shown automatically before the continue button has been clicked.
*/
private boolean continueClicked = false;
/**
* Records whether the Bluetooth adapter was already enabled before we
* asked for Bluetooth discoverability, so we know whether to broadcast a
* {@link BluetoothEnabledEvent}.
*/
private boolean wasAdapterEnabled = false;
private Permission cameraPermission = Permission.UNKNOWN;
private Permission locationPermission = Permission.UNKNOWN;
private boolean isResumed = false, enableWasRequested = false;
private boolean continueClicked, gotCameraPermission;
private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
private BroadcastReceiver bluetoothReceiver = null;
@@ -106,9 +85,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
if (state == null) {
showInitialFragment(IntroFragment.newInstance());
}
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED);
bluetoothReceiver = new BluetoothStateReceiver();
registerReceiver(bluetoothReceiver, filter);
}
@@ -130,40 +107,20 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
}
@Override
public void onStart() {
super.onStart();
// Permissions may have been granted manually while we were stopped
cameraPermission = Permission.UNKNOWN;
locationPermission = Permission.UNKNOWN;
}
@Override
protected void onPostResume() {
super.onPostResume();
isResumed = true;
// Workaround for
// https://code.google.com/p/android/issues/detail?id=190966
showQrCodeFragmentIfAllowed();
if (canShowQrCodeFragment()) showQrCodeFragment();
}
private void showQrCodeFragmentIfAllowed() {
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
if (bluetoothState == BluetoothState.UNKNOWN ||
bluetoothState == BluetoothState.ENABLED) {
requestBluetoothDiscoverable();
} else if (bluetoothState != BluetoothState.WAITING) {
showQrCodeFragment();
}
}
}
private boolean areEssentialPermissionsGranted() {
// If the camera permission has been granted, and the location
// permission has been granted or permanently denied, we can continue
return cameraPermission == Permission.GRANTED &&
(locationPermission == Permission.GRANTED ||
locationPermission == Permission.PERMANENTLY_DENIED);
private boolean canShowQrCodeFragment() {
return isResumed && continueClicked
&& (SDK_INT < 23 || gotCameraPermission)
&& bluetoothState != BluetoothState.UNKNOWN
&& bluetoothState != BluetoothState.WAITING;
}
@Override
@@ -175,54 +132,50 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Override
public void showNextScreen() {
continueClicked = true;
if (checkPermissions()) showQrCodeFragmentIfAllowed();
if (checkPermissions()) {
if (shouldRequestEnableBluetooth()) requestEnableBluetooth();
else if (canShowQrCodeFragment()) showQrCodeFragment();
}
}
private void requestBluetoothDiscoverable() {
private boolean shouldRequestEnableBluetooth() {
return bluetoothState == BluetoothState.UNKNOWN
|| bluetoothState == BluetoothState.REFUSED;
}
private void requestEnableBluetooth() {
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) {
setBluetoothState(BluetoothState.NO_ADAPTER);
} else if (bt.isEnabled()) {
setBluetoothState(BluetoothState.ENABLED);
} else {
enableWasRequested = true;
setBluetoothState(BluetoothState.WAITING);
wasAdapterEnabled = bt.isEnabled();
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
Intent i = new Intent(ACTION_REQUEST_ENABLE);
startActivityForResult(i, REQUEST_ENABLE_BLUETOOTH);
}
}
private void setBluetoothState(BluetoothState bluetoothState) {
LOG.info("Setting Bluetooth state to " + bluetoothState);
this.bluetoothState = bluetoothState;
if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
if (enableWasRequested && bluetoothState == BluetoothState.ENABLED) {
eventBus.broadcast(new BluetoothEnabledEvent());
wasAdapterEnabled = true;
enableWasRequested = false;
}
showQrCodeFragmentIfAllowed();
if (canShowQrCodeFragment()) showQrCodeFragment();
}
@Override
public void onActivityResult(int request, int result, Intent data) {
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
if (result == RESULT_CANCELED) {
setBluetoothState(BluetoothState.REFUSED);
} else {
// If Bluetooth is already discoverable, show the QR code -
// otherwise wait for the state or scan mode to change
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) throw new AssertionError();
if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
setBluetoothState(BluetoothState.DISCOVERABLE);
}
}
// If the request was granted we'll catch the state change event
if (request == REQUEST_ENABLE_BLUETOOTH && result == RESULT_CANCELED)
setBluetoothState(BluetoothState.REFUSED);
}
private void showQrCodeFragment() {
// If we return to the intro fragment, the continue button needs to be
// clicked again before showing the QR code fragment
continueClicked = false;
// If we return to the intro fragment, ask for Bluetooth
// discoverability again before showing the QR code fragment
bluetoothState = BluetoothState.UNKNOWN;
// FIXME #824
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
@@ -241,113 +194,74 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
private boolean checkPermissions() {
if (areEssentialPermissionsGranted()) return true;
// If the camera permission has been permanently denied, ask the
// user to change the setting
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
Builder builder = new Builder(this, R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_camera_title);
builder.setMessage(R.string.permission_camera_denied_body);
builder.setPositiveButton(R.string.ok,
UiUtils.getGoToSettingsListener(this));
builder.setNegativeButton(R.string.cancel,
(dialog, which) -> supportFinishAfterTransition());
builder.show();
if (ContextCompat.checkSelfPermission(this, CAMERA) !=
PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
CAMERA)) {
OnClickListener continueListener =
(dialog, which) -> requestPermission();
Builder builder = new Builder(this, style.BriarDialogTheme);
builder.setTitle(string.permission_camera_title);
builder.setMessage(string.permission_camera_request_body);
builder.setNeutralButton(string.continue_button,
continueListener);
builder.show();
} else {
requestPermission();
}
gotCameraPermission = false;
return false;
}
// Should we show the rationale for one or both permissions?
if (cameraPermission == Permission.SHOW_RATIONALE &&
locationPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_camera_location_title,
R.string.permission_camera_location_request_body);
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_camera_title,
R.string.permission_camera_request_body);
} else if (locationPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_location_title,
R.string.permission_location_request_body);
} else {
requestPermissions();
gotCameraPermission = true;
return true;
}
return false;
}
private void showRationale(@StringRes int title, @StringRes int body) {
Builder builder = new Builder(this, R.style.BriarDialogTheme);
builder.setTitle(title);
builder.setMessage(body);
builder.setNeutralButton(R.string.continue_button,
(dialog, which) -> requestPermissions());
builder.show();
}
private void requestPermissions() {
ActivityCompat.requestPermissions(this,
new String[] {CAMERA, ACCESS_COARSE_LOCATION},
REQUEST_PERMISSION_CAMERA_LOCATION);
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[] {CAMERA},
REQUEST_PERMISSION_CAMERA);
}
@Override
@UiThread
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
throw new AssertionError();
if (gotPermission(CAMERA, permissions, grantResults)) {
cameraPermission = Permission.GRANTED;
} else if (shouldShowRationale(CAMERA)) {
cameraPermission = Permission.SHOW_RATIONALE;
} else {
cameraPermission = Permission.PERMANENTLY_DENIED;
String permissions[], int[] grantResults) {
if (requestCode == REQUEST_PERMISSION_CAMERA) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PERMISSION_GRANTED) {
gotCameraPermission = true;
showNextScreen();
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
CAMERA)) {
// The user has permanently denied the request
OnClickListener cancelListener =
(dialog, which) -> supportFinishAfterTransition();
Builder builder = new Builder(this, style.BriarDialogTheme);
builder.setTitle(string.permission_camera_title);
builder.setMessage(string.permission_camera_denied_body);
builder.setPositiveButton(string.ok,
UiUtils.getGoToSettingsListener(this));
builder.setNegativeButton(string.cancel, cancelListener);
builder.show();
} else {
Toast.makeText(this, string.permission_camera_denied_toast,
LENGTH_LONG).show();
supportFinishAfterTransition();
}
}
}
if (gotPermission(ACCESS_COARSE_LOCATION, permissions, grantResults)) {
locationPermission = Permission.GRANTED;
} else if (shouldShowRationale(ACCESS_COARSE_LOCATION)) {
locationPermission = Permission.SHOW_RATIONALE;
} else {
locationPermission = Permission.PERMANENTLY_DENIED;
}
// If a permission dialog has been shown, showing the QR code fragment
// on this call path would cause a crash due to
// https://code.google.com/p/android/issues/detail?id=190966.
// In that case the isResumed flag prevents the fragment from being
// shown here, and showQrCodeFragmentIfAllowed() will be called again
// from onPostResume().
if (checkPermissions()) showQrCodeFragmentIfAllowed();
}
private boolean gotPermission(String permission, String[] permissions,
int[] grantResults) {
for (int i = 0; i < permissions.length; i++) {
if (permission.equals(permissions[i]))
return grantResults[i] == PERMISSION_GRANTED;
}
return false;
}
private boolean shouldShowRationale(String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(this,
permission);
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON)
setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
} else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
setBluetoothState(BluetoothState.DISCOVERABLE);
else if (scanMode == SCAN_MODE_CONNECTABLE)
setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
}
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
}
}
}

View File

@@ -44,7 +44,6 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
@@ -211,13 +210,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
return true;
});
if (SDK_INT < 27) {
// remove System Default Theme option
List<CharSequence> entries =
new ArrayList<>(Arrays.asList(theme.getEntries()));
entries.remove(getString(R.string.pref_theme_system));
theme.setEntries(entries.toArray(new CharSequence[0]));
}
if (IS_DEBUG_BUILD) {
findPreference("pref_key_explode").setOnPreferenceClickListener(
preference -> {

View File

@@ -20,12 +20,17 @@ import android.widget.TextView;
import org.briarproject.briar.R;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
public class BriarRecyclerView extends FrameLayout {
private static final Logger LOG =
Logger.getLogger(BriarRecyclerView.class.getName());
private final Handler handler = new Handler(Looper.getMainLooper());
private RecyclerView recyclerView;
@@ -34,7 +39,6 @@ public class BriarRecyclerView extends FrameLayout {
private TextView emptyText, emptyAction;
private ProgressBar progressBar;
private RecyclerView.AdapterDataObserver emptyObserver;
@Nullable
private Runnable refresher = null;
private boolean isScrollingToEnd = false;
@@ -213,15 +217,18 @@ public class BriarRecyclerView extends FrameLayout {
throw new IllegalStateException("Need to call setAdapter() first!");
}
refresher = () -> {
LOG.info("Updating Content...");
Adapter adapter = recyclerView.getAdapter();
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
};
LOG.info("Adding Handler Callback");
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
}
public void stopPeriodicUpdate() {
if (refresher != null) {
LOG.info("Removing Handler Callback");
handler.removeCallbacks(refresher);
refresher = null;
}

View File

@@ -1,15 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<!-- Workaround for toolbar icons failing to apply proper layout direction -->
<group
android:pivotX="12"
android:pivotY="12"
android:rotation="180">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</group>
</vector>

View File

@@ -1,6 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path

View File

@@ -471,6 +471,7 @@
<string name="permission_camera_title">إذن الكاميرا</string>
<string name="permission_camera_request_body">للتمكن من مسح رمز QR، يحتاج Briar (براير) إلى إستعمال الكاميرا.</string>
<string name="permission_camera_denied_body">قد رفضت إعطاء إذن الكاميرا، لكن إضافة جهات إتصال يتطلب إستعمال الكاميرا.\n\nالرجاء منح الإذن.</string>
<string name="permission_camera_denied_toast">لم يتم منح الإذن بإستعمال الكاميرا</string>
<string name="qr_code">رمز QR</string>
<string name="show_qr_code_fullscreen">اظهار رمز QR بوضع ملء الشاشة</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Permís de la càmera</string>
<string name="permission_camera_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.</string>
<string name="permission_camera_denied_body">Heu denegat l\'accés a la càmera però per afegir contactes cal utilitzar la càmera.\n\nRecomanem que permeteu l\'accés a la càmera.</string>
<string name="permission_camera_denied_toast">No s\'ha concedit el permís per accedir a la càmera</string>
<string name="qr_code">Codi QR</string>
<string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -403,6 +403,7 @@
<string name="permission_camera_title">Oprávnění pro přístup k fotoaparátu</string>
<string name="permission_camera_request_body">Pro scan QR kódu, Briar vyžaduje přístup k fotoaparátu.</string>
<string name="permission_camera_denied_body">Odmítli jste udělit oprávnění přístupu k fotoaparátu, avšak pro přidání kontaktů je nutné použití fotoaparátu.\n\nZvažte prosím, opětovné udělení přístupu.</string>
<string name="permission_camera_denied_toast">Oprávnění pro přístup k fotoaparátu nebylo uděleno</string>
<string name="qr_code">QR kód</string>
<string name="show_qr_code_fullscreen">Zobrazit QR kód na celou obrazovku</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Berechtigung Kamera</string>
<string name="permission_camera_request_body">Um den QR-Code zu scannen, benötigt Briar Zugriff auf die Kamera.</string>
<string name="permission_camera_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Hinzufügen von Kontakten erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
<string name="qr_code">QR-Code</string>
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Descargar Briar 1.0</string>
<string name="startup_open_database">Descifrando la base de datos...</string>
<string name="startup_migrate_database">Actualizando la base de datos...</string>
<string name="startup_compact_database">Compactando base de datos...</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Abre el panel de navegación</string>
<string name="nav_drawer_close_description">Cierra el panel de navegación</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Permiso de cámara</string>
<string name="permission_camera_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.</string>
<string name="permission_camera_denied_body">Has denegado el acceso a la cámara, pero para añadir contactos se requiere el uso de la cámara.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
<string name="permission_camera_denied_toast">No se concedió el permiso de cámara</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Deskargatu Briar 1.0</string>
<string name="startup_open_database">Datu-basea deszifratzen...</string>
<string name="startup_migrate_database">Datu-basea eguneratzen...</string>
<string name="startup_compact_database">Datu-basea trinkotzen…</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Ireki nabigazio-tiradera</string>
<string name="nav_drawer_close_description">Itxi nabigazio-tiradera</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Kamera erabiltzeko baimena</string>
<string name="permission_camera_request_body">QR kodea eskaneatzeko Briar-ek kamera atzitu behar du.</string>
<string name="permission_camera_denied_body">Kamera atzitzeko baimena ukatu duzu, baina kontaktuak gehitzeko kamera erabili behar da.\n\nMesedez baimendu kamera atzitzea.</string>
<string name="permission_camera_denied_toast">Ez da kamera erabiltzeko baimenik eman</string>
<string name="qr_code">QR kodea</string>
<string name="show_qr_code_fullscreen">Erakutsi QR kodea pantaila osoan</string>
<!--App Locking-->

View File

@@ -474,6 +474,7 @@
<string name="permission_camera_denied_body">شما دسترسی به دوربین را رد کرده اید، اما افزودن مخاطب نیاز به دوربین دارد.
لطفا اجازه دسترسی را بدهید.</string>
<string name="permission_camera_denied_toast">اجازه دسترسی به دوربین پذیرفته نشد</string>
<string name="qr_code">کد QR</string>
<string name="show_qr_code_fullscreen">نمایش کد QR به صورت فول اسکرین</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Kameran käyttöoikeus</string>
<string name="permission_camera_request_body">Skannatakseen QR koodin, Briar tarvitsee luvan käyttää kameraa.</string>
<string name="permission_camera_denied_body">Olet kieltänyt käyttämästä kameraa, mutta yhteyshenkilöiden lisääminen vaatii kameran käyttöä.\n\nOle hyvä ja harkitse kameraluvan myöntämistä.</string>
<string name="permission_camera_denied_toast">Kameran käyttöoikeutta ei myönnetty</string>
<string name="qr_code">QR-koodi</string>
<string name="show_qr_code_fullscreen">Näytä QR-koodi koko näytöllä</string>
<!--App Locking-->

View File

@@ -132,7 +132,7 @@
<string name="qr_code_invalid">Le code QR est invalide</string>
<string name="qr_code_too_old">Le code QR que vous avez lu provient dune version plus ancienne de %s.\n\nVeuillez demander à votre contact de passer à la version la plus récente, puis ressayez.</string>
<string name="qr_code_too_new">Le code QR que vous avez lu provient dune version plus récente de %s.\n\nVeuillez passer à la version la plus récente, puis ressayez.</string>
<string name="camera_error">Erreur de lappareil photo</string>
<string name="camera_error">Erreur de la caméra</string>
<string name="connecting_to_device">Connexion à lappareil\u2026</string>
<string name="authenticating_with_device">Autentification avec lappareil\u2026</string>
<string name="connection_error_title">Impossible de se connecter à votre contact</string>
@@ -433,13 +433,10 @@
<string name="screen_filter_body">Une autre appli saffiche par-dessus Briar. Pour protéger votre sécurité, Briar ne répondra pas au toucher si une autre appli saffiche par-dessus.\n\nLes applis suivantes pourraient safficher par-dessus Briar : \n\n%1$s</string>
<string name="screen_filter_allow">Permettre à ces applis de safficher par-dessus</string>
<!--Permission Requests-->
<string name="permission_camera_title">Accès à lappareil photo</string>
<string name="permission_camera_request_body">Afin de lire le code QR, Briar doit accéder à lappareil photo.</string>
<string name="permission_location_title">Autorisation daccès à la position</string>
<string name="permission_location_request_body">Afin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar nenregistre pas votre position et ne la partage avec personne.</string>
<string name="permission_camera_location_title">Appareil photo et position</string>
<string name="permission_camera_location_request_body">Afin de lire le code QR, Briar doit accéder à lappareil photo.\n\nAfin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar nenregistre pas votre position et ne la partage avec personne.</string>
<string name="permission_camera_title">Accès à la caméra</string>
<string name="permission_camera_request_body">Pour lire le code QR, Briar doit accéder à la caméra.</string>
<string name="permission_camera_denied_body">Vous avez refusé laccès à la caméra, mais lajout de contacts exige lutilisation de celle-ci.\n\nVeuillez envisager dy accorder laccès.</string>
<string name="permission_camera_denied_toast">Laccès à la caméra na pas été accordé</string>
<string name="qr_code">Code QR</string>
<string name="show_qr_code_fullscreen">Afficher le code QR en plein écran</string>
<!--App Locking-->

View File

@@ -436,6 +436,7 @@
<string name="permission_camera_title">Permiso da cámara</string>
<string name="permission_camera_request_body">Para escanear códigos QR, Briar precisa acceso a cámara.</string>
<string name="permission_camera_denied_body">Denegou o permiso de acceso a cámara, pero é necesario para engadir contactos.\n\nPor favor, considere conceder o permiso.</string>
<string name="permission_camera_denied_toast">Non concedeu o acceso a cámara</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Amosar o código QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -53,7 +53,6 @@
<string name="download_briar_button">הורד את Briar 1.0</string>
<string name="startup_open_database">מפענח מסד נתונים...</string>
<string name="startup_migrate_database">משדרג מסד נתונים...</string>
<string name="startup_compact_database">מכווץ מסד נתונים...</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">פתח את מגירת הניווט</string>
<string name="nav_drawer_close_description">סגור את מגירת הניווט</string>
@@ -318,7 +317,6 @@
<string name="blogs_remove_blog_ok">הסר</string>
<string name="blogs_blog_removed">בלוג הוסר</string>
<string name="blogs_reblog_comment_hint">הוסף תגובה (רשותי)</string>
<string name="blogs_reblog_button">פרסם מחדש</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">שתף בלוג</string>
<string name="blogs_sharing_error">הייתה שגיאה בשיתוף בלוג זה.</string>
@@ -463,12 +461,11 @@
<string name="progress_title_logout">מתנתק מן Briar...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">ציפוי מסך התגלה</string>
<string name="screen_filter_body">יישום אחר מציירת מעל Briar. כדי להגן על אבטחתך, Briar לא יגיב לנגיעות כאשר יישום אחר מצייר מעל.\n\nהיישומים הבאים יכולים לצייר מעל:\n\n%1$s</string>
<string name="screen_filter_allow">התר ליישומים אלו לצייר מעל</string>
<!--Permission Requests-->
<string name="permission_camera_title">הרשאת מצלמה</string>
<string name="permission_camera_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך גישה אל המצלמה.</string>
<string name="permission_camera_denied_body">דחית גישה אל המצלמה, אבל הוספת אנשי קשר דורשת שימוש במצלמה.\n\nאנא שקול הענקת גישה.</string>
<string name="permission_camera_denied_toast">הרשאת מצלמה לא הוענקה</string>
<string name="qr_code">קוד QR</string>
<string name="show_qr_code_fullscreen">הראה קוד QR במסך מלא</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">कैमरा अनुमति</string>
<string name="permission_camera_request_body">QR कोड को स्कैन करने के लिए, Briar को कैमरे तक पहुंच की आवश्यकता है।</string>
<string name="permission_camera_denied_body">आपने कैमरे तक पहुंच से वंचित किया है, लेकिन संपर्क जोड़ने के लिए कैमरे का उपयोग करने की आवश्यकता है। \ N \ n कृपया पहुंच प्रदान करने पर विचार करें।</string>
<string name="permission_camera_denied_toast">कैमरा अनुमति नहीं दी गई थी</string>
<string name="qr_code">क्यूआर कोड</string>
<string name="show_qr_code_fullscreen">क्यूआर कोड पूर्णस्क्रीन दिखाएं</string>
<!--App Locking-->

View File

@@ -433,6 +433,7 @@ Vigyázat: Ez végleg törli az identitásait, kapcsolatait és üzeneteit</stri
<string name="permission_camera_title">Kamera jogosultságok</string>
<string name="permission_camera_request_body">A QR kód olvasáshoz a Briar-nak szüksége van kamera hozzáférésre.</string>
<string name="permission_camera_denied_body">Megtiltotta hozzáférést a kamerához, de a kapcsolatok hozzáadásához szükséges a kamera.\n\nKérjük gondolja meg a jog megadását.</string>
<string name="permission_camera_denied_toast">A kamera jogosultság nincs megadva</string>
<string name="qr_code">QR kód</string>
<string name="show_qr_code_fullscreen">A QR kód teljes képernyősen</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Scarica Briar 1.0</string>
<string name="startup_open_database">Decrittazione database...</string>
<string name="startup_migrate_database">Aggiornamento database...</string>
<string name="startup_compact_database">Compattazione database…</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Apri la barra di navigazione</string>
<string name="nav_drawer_close_description">Chiudi la barra di navigazione</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Autorizzazione fotocamera</string>
<string name="permission_camera_request_body">Per scansionare il codice QR, Briar deve accedere alla fotocamera.</string>
<string name="permission_camera_denied_body">Hai negato l\'accesso alla fotocamera, ma questa serve per aggiungere i contatti.\n\nConsidera la possibilità di concedere l\'accesso.</string>
<string name="permission_camera_denied_toast">Autorizzazione fotocamera non concessa</string>
<string name="qr_code">Codice QR</string>
<string name="show_qr_code_fullscreen">Mostra codice QR a tutto schermo</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Cameratoestemming</string>
<string name="permission_camera_request_body">Om de QR-code te scannen moet Briar toegang hebben tot de camera.</string>
<string name="permission_camera_denied_body">Je hebt toegang tot de camera niet vrijgegeven, terwijl het toevoegen van contacten de camera nodig heeft.\n\nOverweeg alsjeblieft toegang vrij te geven.</string>
<string name="permission_camera_denied_toast">Cameratoegang niet vrijgegeven</string>
<string name="qr_code">QR-code</string>
<string name="show_qr_code_fullscreen">Toon QR-code op volledig scherm</string>
<!--App Locking-->

View File

@@ -440,6 +440,7 @@ contactes quavètz partejat aqueste flux quitaràn benlèu de recebre las mes
<string name="permission_camera_title">Permission de la camèra</string>
<string name="permission_camera_request_body">Per numerizar lo còdi QR cal que Briar aja laccès a la camèra.</string>
<string name="permission_camera_denied_body">Avètz regetat laccès a la camèra, mas per ajustar de contacte cal utilizar la camèra.\n\n Mercés de li donar laccès</string>
<string name="permission_camera_denied_toast">La permission a la camèra es pas estada donada</string>
<string name="qr_code">Còdi QR</string>
<string name="show_qr_code_fullscreen">Mostrar lo còdi QR en ecran complèt</string>
<!--App Locking-->

View File

@@ -450,6 +450,7 @@
<string name="permission_camera_title">Dostęp do aparatu</string>
<string name="permission_camera_request_body">Aby zeskanować kod QR, Briar potrzebuje mieć dostęp do aparatu.</string>
<string name="permission_camera_denied_body">Odmówiłeś dostępu do kamery, lecz dodawanie kontaktów tego wymaga.\n\nProszę udzielić dostępu.</string>
<string name="permission_camera_denied_toast">Dostęp do aparatu nie został przyznany</string>
<string name="qr_code">Kod QR</string>
<string name="show_qr_code_fullscreen">Pokaż QR na pełnym ekranie</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Permissão da câmera</string>
<string name="permission_camera_request_body">O Briar precisa acessar a câmera para pode escanear o código QR.</string>
<string name="permission_camera_denied_body">Você negou acesso à câmera, mas para adicionar contatos você precisa da câmera.\n\nPor favor, pense em liberar o acesso a ela.</string>
<string name="permission_camera_denied_toast">A permissão da câmera não foi concedida</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Mostrar o código QR na tela cheia</string>
<!--App Locking-->

View File

@@ -446,6 +446,7 @@
<string name="permission_camera_title">Permisiune de acces la camera foto</string>
<string name="permission_camera_request_body">Pentru a scana codul QR, Briar are nevoie să acceseze camera foto.</string>
<string name="permission_camera_denied_body">Ați refuzat accesul la camera foto, dar pentru a adăuga contacte este necesară folosirea camerei foto.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="permission_camera_denied_toast">Permisiunea de acces la camera foto nu a fost acordată</string>
<string name="qr_code">Cod QR</string>
<string name="show_qr_code_fullscreen">Arată codul QR pe tot ecranul</string>
<!--App Locking-->

View File

@@ -452,6 +452,7 @@
<string name="permission_camera_title">Доступ к камере</string>
<string name="permission_camera_request_body">Для сканирования QR-кода Briar необходим доступ к камере.</string>
<string name="permission_camera_denied_body">Вы отказали в доступе к камере. Для добавления контактов необходимо использовать камеру.\n\nРассмотрите возможность предоставления доступа.</string>
<string name="permission_camera_denied_toast">Доступ к камере предоставлен не был</string>
<string name="qr_code">QR-код</string>
<string name="show_qr_code_fullscreen">Показать QR-код во весь экран</string>
<!--App Locking-->

View File

@@ -436,6 +436,7 @@
<string name="permission_camera_title">Leje mbi kamerën</string>
<string name="permission_camera_request_body">Që të skanojë kodin QR, Briar-i lypset të hyjë te kamera.</string>
<string name="permission_camera_denied_body">Keni mohuar hyrjen në kamera, por shtimi i kontakteve lyp përdorimin e kamerës.\n\nJu lutemi, shihni mundësinë e akordimit të hyrjes.</string>
<string name="permission_camera_denied_toast">S\u akorduan leje mbi kamerën</string>
<string name="qr_code">Kod QR</string>
<string name="show_qr_code_fullscreen">Shfaqe kodin QR sa tërë ekrani</string>
<!--App Locking-->

View File

@@ -466,6 +466,7 @@ Slijedeće aplikacije mogu precrtavati preko:
<string name="permission_camera_denied_body">Uskratili ste pristup kameri, ali dodavanje kontakata zahtijeva pristup kameri.
Molim vas da uzmete u obzir odobravanja pristupa.</string>
<string name="permission_camera_denied_toast">Dozvola kameri nije odobrena</string>
<string name="qr_code">QR kod</string>
<string name="show_qr_code_fullscreen">Prikazi QR kod na puni ekran</string>
<!--App Locking-->

View File

@@ -420,6 +420,7 @@
<string name="permission_camera_title">相机权限</string>
<string name="permission_camera_request_body">Briar 需要获得相机权限以扫描二维码。</string>
<string name="permission_camera_denied_body">您已拒绝相机权限,而添加联系人需要使用相机。\n\n请考虑授予相机权限。</string>
<string name="permission_camera_denied_toast">未授予相机权限</string>
<string name="qr_code">二维码</string>
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
<!--App Locking-->

View File

@@ -63,10 +63,10 @@
<item>@string/pref_theme_auto</item>
<item>@string/pref_theme_system</item>
</string-array>
<string name="pref_theme_light_value" translatable="false">light</string>
<string name="pref_theme_dark_value" translatable="false">dark</string>
<string name="pref_theme_auto_value" translatable="false">auto</string>
<string name="pref_theme_system_value" translatable="false">system</string>
<string name="pref_theme_light_value">light</string>
<string name="pref_theme_dark_value">dark</string>
<string name="pref_theme_auto_value">auto</string>
<string name="pref_theme_system_value">system</string>
<string-array name="pref_theme_values">
<item>@string/pref_theme_light_value</item>
<item>@string/pref_theme_dark_value</item>
@@ -82,8 +82,8 @@
<item>@string/pref_lock_timeout_30</item>
<item>@string/pref_lock_timeout_60</item>
</string-array>
<string name="pref_lock_timeout_value_default" translatable="false">5</string>
<string name="pref_lock_timeout_value_never" translatable="false">-1</string>
<string name="pref_lock_timeout_value_default">5</string>
<string name="pref_lock_timeout_value_never">-1</string>
<string-array name="pref_key_lock_timeout_values">
<item>@string/pref_lock_timeout_value_never</item>
<item>1</item>

View File

@@ -468,11 +468,8 @@
<!-- Permission Requests -->
<string name="permission_camera_title">Camera permission</string>
<string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string>
<string name="permission_location_title">Location permission</string>
<string name="permission_location_request_body">To discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
<string name="permission_camera_location_title">Camera and location</string>
<string name="permission_camera_location_request_body">To scan the QR code, Briar needs access to the camera.\n\nTo discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
<string name="permission_camera_denied_toast">Camera permission was not granted</string>
<string name="qr_code">QR code</string>
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>

View File

@@ -72,16 +72,9 @@ Returns a JSON array of contacts:
The only workaround is to add a contact to the Briar app running on a rooted Android phone
and then move its database (and key files) to the headless peer.
### Removing a contact
`DELETE /v1/contacts/{contactId}`
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns with a status code `200`, if removal was successful.
### Listing all private messages
`GET /v1/messages/{contactId}`
`GET /messages/{contactId}`
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns a JSON array of private messages:
@@ -107,7 +100,7 @@ Attention: There can messages of other `type`s where the message `text` is `null
### Writing a private message
`POST /v1/messages/{contactId}`
`POST /messages/{contactId}`
The text of the message should be posted as JSON:

View File

@@ -1,8 +1,8 @@
plugins {
id 'java'
id 'idea'
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
id 'org.jetbrains.kotlin.kapt' version '1.2.71'
id 'org.jetbrains.kotlin.jvm' version '1.2.70'
id 'org.jetbrains.kotlin.kapt' version '1.2.70'
id 'witness'
}
apply from: 'witness.gradle'
@@ -14,9 +14,9 @@ dependencies {
implementation project(path: ':briar-core', configuration: 'default')
implementation project(path: ':bramble-java', configuration: 'default')
implementation 'io.javalin:javalin:2.3.0'
implementation 'io.javalin:javalin:2.2.0'
implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.7'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
implementation 'com.github.ajalt:clikt:1.5.0'
kapt 'com.google.dagger:dagger-compiler:2.0.2'
@@ -24,11 +24,11 @@ dependencies {
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
def junitVersion = '5.3.1'
def junitVersion = '5.2.0'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
testImplementation "io.mockk:mockk:1.8.9"
testImplementation "io.mockk:mockk:1.8.6"
testImplementation "org.skyscreamer:jsonassert:1.5.0"
}

View File

@@ -7,11 +7,9 @@ import io.javalin.Context
import io.javalin.Javalin
import io.javalin.JavalinEvent.SERVER_START_FAILED
import io.javalin.JavalinEvent.SERVER_STOPPED
import io.javalin.NotFoundResponse
import io.javalin.apibuilder.ApiBuilder.*
import io.javalin.core.util.ContextUtil
import io.javalin.core.util.Header.AUTHORIZATION
import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.briar.headless.blogs.BlogController
import org.briarproject.briar.headless.contact.ContactController
import org.briarproject.briar.headless.event.WebSocketController
@@ -52,6 +50,7 @@ constructor(
.event(SERVER_START_FAILED) {serverStopped() }
.event(SERVER_STOPPED) { serverStopped() }
if (debug) app.enableDebugLogging()
app.start()
app.accessManager { handler, ctx, _ ->
if (ctx.header(AUTHORIZATION) == "Bearer $authToken") {
@@ -64,9 +63,6 @@ constructor(
path("/v1") {
path("/contacts") {
get { ctx -> contactController.list(ctx) }
path("/:contactId") {
delete { ctx -> contactController.delete(ctx) }
}
}
path("/messages/:contactId") {
get { ctx -> messagingController.list(ctx) }
@@ -101,7 +97,6 @@ constructor(
webSocketController.sessions.remove(session)
}
}
app.start()
}
private fun serverStopped() {
@@ -117,21 +112,6 @@ constructor(
}
/**
* Returns a [ContactId] from the "contactId" path parameter.
*
* @throws NotFoundResponse when contactId is not a number.
*/
fun Context.getContactIdFromPathParam(): ContactId {
val contactString = pathParam("contactId")
val contactInt = try {
Integer.parseInt(contactString)
} catch (e: NumberFormatException) {
throw NotFoundResponse()
}
return ContactId(contactInt)
}
/**
* Returns a String from the JSON field or throws [BadRequestResponse] if null or empty.
*/

View File

@@ -5,6 +5,5 @@ import io.javalin.Context
interface ContactController {
fun list(ctx: Context): Context
fun delete(ctx: Context): Context
}

View File

@@ -1,10 +1,7 @@
package org.briarproject.briar.headless.contact
import io.javalin.Context
import io.javalin.NotFoundResponse
import org.briarproject.bramble.api.contact.ContactManager
import org.briarproject.bramble.api.db.NoSuchContactException
import org.briarproject.briar.headless.getContactIdFromPathParam
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
@@ -22,14 +19,4 @@ constructor(private val contactManager: ContactManager) : ContactController {
return ctx.json(contacts)
}
override fun delete(ctx: Context): Context {
val contactId = ctx.getContactIdFromPathParam()
try {
contactManager.removeContact(contactId)
} catch (e: NoSuchContactException) {
throw NotFoundResponse()
}
return ctx
}
}

View File

@@ -26,7 +26,6 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse
import org.briarproject.briar.headless.event.WebSocketController
import org.briarproject.briar.headless.event.output
import org.briarproject.briar.headless.getContactIdFromPathParam
import org.briarproject.briar.headless.getFromJson
import org.briarproject.briar.headless.json.JsonDict
import java.util.concurrent.Executor
@@ -85,7 +84,13 @@ constructor(
}
private fun getContact(ctx: Context): Contact {
val contactId = ctx.getContactIdFromPathParam()
val contactString = ctx.pathParam("contactId")
val contactInt = try {
Integer.parseInt(contactString)
} catch (e: NumberFormatException) {
throw NotFoundResponse()
}
val contactId = ContactId(contactInt)
return try {
contactManager.getContact(contactId)
} catch (e: NoSuchContactException) {

View File

@@ -1,16 +1,10 @@
package org.briarproject.briar.headless.contact
import io.javalin.NotFoundResponse
import io.javalin.json.JavalinJson.toJson
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import org.briarproject.bramble.api.contact.Contact
import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.bramble.api.db.NoSuchContactException
import org.briarproject.bramble.identity.output
import org.briarproject.briar.headless.ControllerTest
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
internal class ContactControllerTest : ControllerTest() {
@@ -31,30 +25,6 @@ internal class ContactControllerTest : ControllerTest() {
controller.list(ctx)
}
@Test
fun testDelete() {
every { ctx.pathParam("contactId") } returns "1"
every { contactManager.removeContact(ContactId(1)) } just Runs
controller.delete(ctx)
}
@Test
fun testDeleteInvalidContactId() {
every { ctx.pathParam("contactId") } returns "foo"
assertThrows(NotFoundResponse::class.java) {
controller.delete(ctx)
}
}
@Test
fun testDeleteNonexistentContactId() {
every { ctx.pathParam("contactId") } returns "1"
every { contactManager.removeContact(ContactId(1)) } throws NoSuchContactException()
assertThrows(NotFoundResponse::class.java) {
controller.delete(ctx)
}
}
@Test
fun testOutputContact() {
val json = """

View File

@@ -1,26 +1,26 @@
dependencyVerification {
verify = [
'com.fasterxml.jackson.core:jackson-annotations:2.9.0:jackson-annotations-2.9.0.jar:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a',
'com.fasterxml.jackson.core:jackson-core:2.9.7:jackson-core-2.9.7.jar:9e5bc0efabd9f0cac5c1fdd9ae35b16332ed22a0ee19a356de370a18a8cb6c84',
'com.fasterxml.jackson.core:jackson-databind:2.9.7:jackson-databind-2.9.7.jar:675376decfc070b039d2be773a97002f1ee1e1346d95bd99feee0d56683a92bf',
'com.fasterxml.jackson.core:jackson-core:2.9.6:jackson-core-2.9.6.jar:fab8746aedd6427788ee390ea04d438ec141bff7eb3476f8bdd5d9110fb2718a',
'com.fasterxml.jackson.core:jackson-databind:2.9.6:jackson-databind-2.9.6.jar:657e3e979446d61f88432b9c50f0ccd9c1fe4f1c822d533f5572e4c0d172a125',
'com.github.ajalt:clikt:1.5.0:clikt-1.5.0.jar:f13ab614cb0372229f6bb1e19aa98ee6f4ac96f253d0e72d482ee4f5fd2a13a9',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'com.vaadin.external.google:android-json:0.0.20131108.vaadin1:android-json-0.0.20131108.vaadin1.jar:dfb7bae2f404cfe0b72b4d23944698cb716b7665171812a0a4d0f5926c0fac79',
'io.javalin:javalin:2.3.0:javalin-2.3.0.jar:3571e83863e1f163854f1b2ee3cbfc1336fcbdfa595ec9c2ed8ab8bfa792e5f4',
'io.mockk:mockk-agent-api:1.8.9:mockk-agent-api-1.8.9.jar:8cbc0adc0f349f891d44ffdaba55c68271790ce9c33cc437d2be86a5af572d6c',
'io.mockk:mockk-agent-common:1.8.9:mockk-agent-common-1.8.9.jar:a67b8bb696f91023e4446e2c8594c652924a350422047c96f31031be2a8de54a',
'io.mockk:mockk-agent-jvm:1.8.9:mockk-agent-jvm-1.8.9.jar:4a8b83501e0fda6f79601e073c9e419dc649eeb8d18645ae0d60c64f1e671076',
'io.mockk:mockk-common:1.8.9:mockk-common-1.8.9.jar:a3fb4d3fa776dde4eab2cc49e5df68fa394064df47431e2add9fc2100f3a145f',
'io.mockk:mockk-dsl-jvm:1.8.9:mockk-dsl-jvm-1.8.9.jar:d6cfda3ba94b3fba2826c4554a33866a5559df9326fb28b559078fcb4508e60d',
'io.mockk:mockk-dsl:1.8.9:mockk-dsl-1.8.9.jar:e24f0de13e12227544a68dae13f4fae4dd376f5d1eac796d3f4594989c0f2e68',
'io.mockk:mockk:1.8.9:mockk-1.8.9.jar:9ead145f12b086af1d15eb4bf0fa5ccee02781b69937be6f77f47ac03100f526',
'io.javalin:javalin:2.2.0:javalin-2.2.0.jar:f7298fa281400559e92f000477a631c75aca9e01776962fd4b392fdb3b714190',
'io.mockk:mockk-agent-api:1.8.6:mockk-agent-api-1.8.6.jar:613512c66538e6349e03df641a868f4ee324f13e2e1dbd67a0ed388aa664a444',
'io.mockk:mockk-agent-common:1.8.6:mockk-agent-common-1.8.6.jar:cb7cb26fae5bfd3c89090858548990f311b27f673b9efa9d0c94f97c463b2863',
'io.mockk:mockk-agent-jvm:1.8.6:mockk-agent-jvm-1.8.6.jar:3f30b98d23ada8b5a44d75b43cd58fc03252fcb96939ff31e7ad659818af1e5d',
'io.mockk:mockk-common:1.8.6:mockk-common-1.8.6.jar:a04b0e2fc7d583807cf89f3bbf5c7501808725e49e385d95486e1008d8ab2ba8',
'io.mockk:mockk-dsl-jvm:1.8.6:mockk-dsl-jvm-1.8.6.jar:c2c5df747ff04d1a3e02212b7b43f9ba4233597ae278928598275d7a7bb26d73',
'io.mockk:mockk-dsl:1.8.6:mockk-dsl-1.8.6.jar:f6014265fe88ef1290c936741bdd0a7c3d9ceba9ee3bd2a153d65b05e1fc7946',
'io.mockk:mockk:1.8.6:mockk-1.8.6.jar:0a200d71bab11facfe50637b1980f53c07a21bfa4dd9eb021ac8e8cc693924b2',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.servlet:javax.servlet-api:3.1.0:javax.servlet-api-3.1.0.jar:af456b2dd41c4e82cf54f3e743bc678973d9fe35bd4d3071fa05c7e5333b8482',
'net.bytebuddy:byte-buddy-agent:1.8.22:byte-buddy-agent-1.8.22.jar:ebc20e83fbb13e7911e4c704c9548a4166d7e83922f80de700ae5c5c983943d5',
'net.bytebuddy:byte-buddy:1.8.22:byte-buddy-1.8.22.jar:d330d2ef290a2852bbaf06eab03bc93d24501599c8a836da9d946f82c48e276c',
'net.bytebuddy:byte-buddy-agent:1.8.8:byte-buddy-agent-1.8.8.jar:dc1a2dcefe72731fa89ae84e32231c74d545ccf8216c79865096e546f20c57e8',
'net.bytebuddy:byte-buddy:1.8.8:byte-buddy-1.8.8.jar:30aed1ae2ee5261b1d2f0e98ec3fcb40755c3f61b378089fb65d56098df1f16b',
'org.apiguardian:apiguardian-api:1.0.0:apiguardian-api-1.0.0.jar:1f58b77470d8d147a0538d515347dd322f49a83b9e884b8970051160464b65b3',
'org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830:websocket-api-9.4.12.v20180830.jar:6f7ecb42601058ffe4a6c19c5340cac3ebf0f83e2e252b457558f104238278e3',
'org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830:websocket-client-9.4.12.v20180830.jar:97c6882c858a75776773eaccc01739757c4e9f60a51613878c1f2b2ba03d91af',
@@ -36,30 +36,31 @@ dependencyVerification {
'org.eclipse.jetty:jetty-util:9.4.12.v20180830:jetty-util-9.4.12.v20180830.jar:60ad53e118a3e7d10418b155b9944d90b2e4e4c732e53ef4f419473288d3f48c',
'org.eclipse.jetty:jetty-webapp:9.4.12.v20180830:jetty-webapp-9.4.12.v20180830.jar:5301e412a32bf7dddcfad458d952179597c61f8fd531c265873562725c3d4646',
'org.eclipse.jetty:jetty-xml:9.4.12.v20180830:jetty-xml-9.4.12.v20180830.jar:5b8298ab3d43ddaf0941d41f51b82c8ae23a247da055fa161b752ab9495155ed',
'org.jetbrains.kotlin:kotlin-android-extensions:1.2.71:kotlin-android-extensions-1.2.71.jar:6c3f52bbb7c25ddb102cfbe1e91e3f1ee45805f842310fb92496668dbf1366de',
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.71:kotlin-annotation-processing-gradle-1.2.71.jar:107070d5bc10530a6823907487609899ac4d8753f730b8abf1f158971d93802e',
'org.jetbrains.kotlin:kotlin-build-common:1.2.71:kotlin-build-common-1.2.71.jar:f3c6874442409c5fac7142dc5a4f7a0148147896a6d7f9e0c3cb140690090cb1',
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.71:kotlin-compiler-embeddable-1.2.71.jar:623e546310d3da89ed2bc0cf9f7fedd78fadd9cd65d2ff798fa894c14e527665',
'org.jetbrains.kotlin:kotlin-compiler-runner:1.2.71:kotlin-compiler-runner-1.2.71.jar:7259a24562ba880f821bc7c9ae51f724d66e8ea00f6e3143d4427a161ae6e998',
'org.jetbrains.kotlin:kotlin-daemon-client:1.2.71:kotlin-daemon-client-1.2.71.jar:2ad669e3c2cedd3a2406f3c2c4739dba537acf373d605c207ed5058630e66462',
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.2.71:kotlin-gradle-plugin-api-1.2.71.jar:a141e6b9740ebf88ce53d258e40cb59f54ee8e2fafdff4c4c10b0abdceb57cba',
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.2.71:kotlin-gradle-plugin-model-1.2.71.jar:7082e9858b3ead143bcb48383007751a776fd46e8ce4cec1a1d4a873db5e7ec1',
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.71:kotlin-gradle-plugin-1.2.71.jar:ab88dab9bd2a3c31c4e6aaa0eb4168d13f126e9076561bb42f5f3fe80c902a78',
'org.jetbrains.kotlin:kotlin-reflect:1.2.71:kotlin-reflect-1.2.71.jar:1f3e10abd68d0b0816bddab7314f61269e01d8aa2ca1cbd120c12d3b4dc94b0f',
'org.jetbrains.kotlin:kotlin-script-runtime:1.2.71:kotlin-script-runtime-1.2.71.jar:307d0a56734458a5e57e3ea788c15b22591912ba39f81b2cc8b0a090944012bb',
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.71:kotlin-scripting-compiler-embeddable-1.2.71.jar:97a5d94241d13719ea756a908262a10c638db7a8caaa23b28537b1687f894f0c',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
'org.jetbrains.kotlin:kotlin-android-extensions:1.2.70:kotlin-android-extensions-1.2.70.jar:534601f4b10bd175268a611ea370051f75db3377da11e9400430f01db7c70bd0',
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.70:kotlin-annotation-processing-gradle-1.2.70.jar:820da7e3637066c14eb3d54dc29cd6d4dc4a041ff603d0b15844403de47b7d12',
'org.jetbrains.kotlin:kotlin-build-common:1.2.70:kotlin-build-common-1.2.70.jar:6756d98108512b8a42013e453912e868777a20f05cac3f40af891e4058e94da6',
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.70:kotlin-compiler-embeddable-1.2.70.jar:8958d6f6ce4e49a6cecaaa9a1711a6b03df793fe066a74d88cf4958f20b0f10d',
'org.jetbrains.kotlin:kotlin-compiler-runner:1.2.70:kotlin-compiler-runner-1.2.70.jar:44654c5f86e2b36222cb8b09e5eeb0b252db1e83d258b27fb31c670c339e65e1',
'org.jetbrains.kotlin:kotlin-daemon-client:1.2.70:kotlin-daemon-client-1.2.70.jar:92b73c45670324ae6b81ccaa123a38eddd53adfe3a2b58938421d4d17a53f9e3',
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.2.70:kotlin-gradle-plugin-api-1.2.70.jar:a60b71d564795461e023956d8b3dd03d606d51ad1eb79e7c002dbce62a24378c',
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.2.70:kotlin-gradle-plugin-model-1.2.70.jar:a7fdb4b35e7537ad417e6b19595a1df7d676e8e9fa5a8a96dbcf1a40b577493b',
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.70:kotlin-gradle-plugin-1.2.70.jar:b9a96b3d39215b7fdcc8013476e70f86b8010277dd3639e58b42e6ffde1f3cab',
'org.jetbrains.kotlin:kotlin-reflect:1.2.41:kotlin-reflect-1.2.41.jar:1bab75771dfa2bb5949cd383ceaedf6f8d354fa0d677804fc5a39e320bab70d3',
'org.jetbrains.kotlin:kotlin-reflect:1.2.70:kotlin-reflect-1.2.70.jar:89ef46a458c5ee58b8460d0d22b0bb484eea0589ccffd59d650ef38bcb60e806',
'org.jetbrains.kotlin:kotlin-script-runtime:1.2.70:kotlin-script-runtime-1.2.70.jar:0124dfcf890e39250c3a4481cdd27f038d8321e93ce983730c4cbc10143eadc2',
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.70:kotlin-scripting-compiler-embeddable-1.2.70.jar:cbd88e1cae3f8f2baeb7021d3b76323b30e82663e7b4222a214f33ee314b3653',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70:kotlin-stdlib-common-1.2.70.jar:bb6898bef18e1de5d416d5135ca70dcac6645718c7d84bbddcfeb76ed1c199a1',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.70:kotlin-stdlib-jdk7-1.2.70.jar:b4ace315288134b52fddb58b4a92636faafb2ab5eb46bad97d3bce7623a8e213',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.70:kotlin-stdlib-jdk8-1.2.70.jar:88d0c29f4065b6ad34261fb4a04d39f9813051c6942428d718b649378d057ad1',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.70:kotlin-stdlib-1.2.70.jar:7d20d0a56dd0ea6176137759a6aad331bbfae67436b45e5f0a4d8dafb6985c81',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.junit.jupiter:junit-jupiter-api:5.3.1:junit-jupiter-api-5.3.1.jar:7923e21f030a9964d70a0e48007ca873280c66ddf0f0620b2d969852c23d5653',
'org.junit.jupiter:junit-jupiter-engine:5.3.1:junit-jupiter-engine-5.3.1.jar:04f4354548a30827e126bdf6fcbe3640789ad8335a6f3f0762bf7f9f74e51fbf',
'org.junit.jupiter:junit-jupiter-params:5.3.1:junit-jupiter-params-5.3.1.jar:72fe344712d4cd88dd0cb4bfa304322d512d2cb27173ed64cb5036a573d29f4c',
'org.junit.platform:junit-platform-commons:1.3.1:junit-platform-commons-1.3.1.jar:457d8e1c0c80d1e320a792ec35e7c180694ba05182d7ecf7dabdb4e5a8a12fe2',
'org.junit.platform:junit-platform-engine:1.3.1:junit-platform-engine-1.3.1.jar:303d0546c3e950cc3beaca00dfcbf2632d4eca530e4e446391bf193cbc2a71a3',
'org.junit.jupiter:junit-jupiter-api:5.2.0:junit-jupiter-api-5.2.0.jar:47f7d71b35dc331210b9ab219bbb00d54332981aa12eb5effe817de17e1ae7b3',
'org.junit.jupiter:junit-jupiter-engine:5.2.0:junit-jupiter-engine-5.2.0.jar:8f994f4094790e246dc84de86a1ff4194ca85e8b13bedaca0207f727ebfbc813',
'org.junit.jupiter:junit-jupiter-params:5.2.0:junit-jupiter-params-5.2.0.jar:34ce02519044ef68217002f640a83e267c4001ce53b68270218d49d00449a836',
'org.junit.platform:junit-platform-commons:1.2.0:junit-platform-commons-1.2.0.jar:7771af2f797d1d0ccce9920eb3cd826fb8fd7659ccb4d8877e76d9412be72cc2',
'org.junit.platform:junit-platform-engine:1.2.0:junit-platform-engine-1.2.0.jar:60b102e94ea01556fdc8c041950a05450edc188e3708f032a6bfb1a50ba0bc22',
'org.objenesis:objenesis:2.6:objenesis-2.6.jar:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
'org.opentest4j:opentest4j:1.1.1:opentest4j-1.1.1.jar:f106351abd941110226745ed103c85863b3f04e9fa82ddea1084639ae0c5336c',
'org.opentest4j:opentest4j:1.1.0:opentest4j-1.1.0.jar:65a5fd7380f53aac708bcee3091dbe2dba73a9a2e7645b66e70e0804fc36ee3b',
'org.skyscreamer:jsonassert:1.5.0:jsonassert-1.5.0.jar:a310bc79c3f4744e2b2e993702fcebaf3696fec0063643ffdc6b49a8fb03ef39',
'org.slf4j:slf4j-api:1.7.25:slf4j-api-1.7.25.jar:18c4a0095d5c1da6b817592e767bb23d29dd2f560ad74df75ff3961dbde25b79',
'org.slf4j:slf4j-simple:1.7.25:slf4j-simple-1.7.25.jar:0966e86fffa5be52d3d9e7b89dd674d98a03eed0a454fbaf7c1bd9493bd9d874',