mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
82 Commits
feature-fl
...
alpha-1.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6a284bd6d | ||
|
|
9d4d992009 | ||
|
|
e92eb1c699 | ||
|
|
07e56f7086 | ||
|
|
fe31e60e66 | ||
|
|
7810e7e848 | ||
|
|
2566105f13 | ||
|
|
cab8f834bd | ||
|
|
ec0a754289 | ||
|
|
e81fe44ea1 | ||
|
|
e399b9196a | ||
|
|
aadbd3a662 | ||
|
|
f4fd65aee4 | ||
|
|
61e7d2ebf9 | ||
|
|
06dd8c65aa | ||
|
|
2f351b318e | ||
|
|
a468af94db | ||
|
|
49f10e7e82 | ||
|
|
01b1741e83 | ||
|
|
b7003a3587 | ||
|
|
3dbf327937 | ||
|
|
73d806f8b9 | ||
|
|
f1ae57b213 | ||
|
|
cae9efb4bf | ||
|
|
39ac737015 | ||
|
|
edd3310d03 | ||
|
|
a09d88daa8 | ||
|
|
3dc984659d | ||
|
|
f580525734 | ||
|
|
fbf0f63ff7 | ||
|
|
ee9234e12e | ||
|
|
2657e2bc08 | ||
|
|
3c40c11dfb | ||
|
|
3bdbabf38a | ||
|
|
a378c24af8 | ||
|
|
b09ea495e7 | ||
|
|
070165f608 | ||
|
|
445f174275 | ||
|
|
ea5af72878 | ||
|
|
ecf2e75424 | ||
|
|
feebd89029 | ||
|
|
cf723f8002 | ||
|
|
b8e743021c | ||
|
|
b785b6c10f | ||
|
|
26ec200f50 | ||
|
|
82efb0d044 | ||
|
|
4ac4ba13d4 | ||
|
|
0844cd3547 | ||
|
|
69e6648ded | ||
|
|
518aeb38b9 | ||
|
|
7e5e61fc05 | ||
|
|
6ecb44bcaa | ||
|
|
f02bbebf6c | ||
|
|
b8612715f8 | ||
|
|
b86ddfa22f | ||
|
|
0dd4d86f4a | ||
|
|
17e0829f42 | ||
|
|
938d8b71a0 | ||
|
|
1b808584b6 | ||
|
|
36db5b48ef | ||
|
|
ccd6ed9ff0 | ||
|
|
0ced10b3a9 | ||
|
|
98064e9efe | ||
|
|
63172ef2e4 | ||
|
|
7a854e70cb | ||
|
|
ac8a4db457 | ||
|
|
5a09530670 | ||
|
|
4fe91bacc6 | ||
|
|
7f70a1519b | ||
|
|
c92ee0458e | ||
|
|
10b1fe756d | ||
|
|
1a2a250be0 | ||
|
|
a621b8077e | ||
|
|
19084d4060 | ||
|
|
2f73ee1b57 | ||
|
|
45fa12c0b3 | ||
|
|
4253bbaaf5 | ||
|
|
8c2e58796b | ||
|
|
3f13e7e9c3 | ||
|
|
421a93b9a6 | ||
|
|
8a088638db | ||
|
|
a888c5f632 |
@@ -83,29 +83,14 @@ android test:
|
|||||||
test_reproducible:
|
test_reproducible:
|
||||||
stage: check_reproducibility
|
stage: check_reproducibility
|
||||||
script:
|
script:
|
||||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[APP]='briar' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||||
|
- "curl -X POST -F token=${RELEASE_JAR_CHECK_TOKEN} -F ref=main -F variables[APP]='briar-headless' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/307/trigger/pipeline"
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
|
|
||||||
.optional_tests:
|
mailbox integration test:
|
||||||
stage: optional_tests
|
stage: optional_tests
|
||||||
extends: .base-test
|
extends: .base-test
|
||||||
|
|
||||||
bridge test:
|
|
||||||
extends: .optional_tests
|
|
||||||
rules:
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
|
||||||
when: on_success
|
|
||||||
allow_failure: false
|
|
||||||
- if: '$CI_COMMIT_TAG == null'
|
|
||||||
when: manual
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
|
||||||
timeout: 4h
|
|
||||||
|
|
||||||
mailbox integration test:
|
|
||||||
extends: .optional_tests
|
|
||||||
rules:
|
rules:
|
||||||
- changes:
|
- changes:
|
||||||
- mailbox-integration-tests/**/*
|
- mailbox-integration-tests/**/*
|
||||||
|
|||||||
28
.idea/runConfigurations/BridgeTest.xml
generated
28
.idea/runConfigurations/BridgeTest.xml
generated
@@ -1,28 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="BridgeTest" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="env">
|
|
||||||
<map>
|
|
||||||
<entry key="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="--tests "org.briarproject.bramble.plugin.tor.BridgeTest"" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value=":bramble-java:test" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -13,8 +13,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 10420
|
versionCode 10500
|
||||||
versionName "1.4.20"
|
versionName "1.5.0"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -40,6 +40,8 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api 'org.briarproject:dont-kill-me-lib:0.2.6'
|
||||||
|
|
||||||
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
||||||
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
||||||
// even though the bramble-api test classes are provided by the testImplementation dependency
|
// even though the bramble-api test classes are provided by the testImplementation dependency
|
||||||
@@ -49,6 +51,7 @@ dependencies {
|
|||||||
implementation project(':bramble-core')
|
implementation project(':bramble-core')
|
||||||
|
|
||||||
implementation 'androidx.annotation:annotation:1.5.0'
|
implementation 'androidx.annotation:annotation:1.5.0'
|
||||||
|
implementation "org.briarproject:onionwrapper-android:$onionwrapper_version"
|
||||||
|
|
||||||
tor "org.briarproject:tor-android:$tor_version"
|
tor "org.briarproject:tor-android:$tor_version"
|
||||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||||
@@ -71,7 +74,7 @@ def torLibsDir = 'src/main/jniLibs'
|
|||||||
task cleanTorBinaries {
|
task cleanTorBinaries {
|
||||||
outputs.dir torLibsDir
|
outputs.dir torLibsDir
|
||||||
doLast {
|
doLast {
|
||||||
delete fileTree(torLibsDir) { include '**/*.so' }
|
delete fileTree(torLibsDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,34 +83,9 @@ clean.dependsOn cleanTorBinaries
|
|||||||
task unpackTorBinaries {
|
task unpackTorBinaries {
|
||||||
outputs.dir torLibsDir
|
outputs.dir torLibsDir
|
||||||
doLast {
|
doLast {
|
||||||
configurations.tor.each { outer ->
|
copy {
|
||||||
zipTree(outer).each { inner ->
|
from configurations.tor.collect { zipTree(it) }
|
||||||
if (inner.name.endsWith('_arm_pie.zip')) {
|
into torLibsDir
|
||||||
copy {
|
|
||||||
from zipTree(inner)
|
|
||||||
into torLibsDir
|
|
||||||
rename '(.*)', 'armeabi-v7a/lib$1.so'
|
|
||||||
}
|
|
||||||
} else if (inner.name.endsWith('_arm64_pie.zip')) {
|
|
||||||
copy {
|
|
||||||
from zipTree(inner)
|
|
||||||
into torLibsDir
|
|
||||||
rename '(.*)', 'arm64-v8a/lib$1.so'
|
|
||||||
}
|
|
||||||
} else if (inner.name.endsWith('_x86_pie.zip')) {
|
|
||||||
copy {
|
|
||||||
from zipTree(inner)
|
|
||||||
into torLibsDir
|
|
||||||
rename '(.*)', 'x86/lib$1.so'
|
|
||||||
}
|
|
||||||
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
|
|
||||||
copy {
|
|
||||||
from zipTree(inner)
|
|
||||||
into torLibsDir
|
|
||||||
rename '(.*)', 'x86_64/lib$1.so'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependsOn cleanTorBinaries
|
dependsOn cleanTorBinaries
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 -->
|
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 and Nubia devices running API 32 -->
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BLUETOOTH"
|
android:name="android.permission.BLUETOOTH"
|
||||||
android:maxSdkVersion="31" />
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||||
android:maxSdkVersion="30" />
|
android:maxSdkVersion="30" />
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.briarproject.android.dontkillmelib.wakelock;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class AndroidWakeLockModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
AndroidWakeLockManager provideWakeLockManager(
|
||||||
|
AndroidWakeLockManagerImpl wakeLockManager) {
|
||||||
|
return wakeLockManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockModule;
|
||||||
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
||||||
import org.briarproject.bramble.io.DnsModule;
|
import org.briarproject.bramble.io.DnsModule;
|
||||||
import org.briarproject.bramble.network.AndroidNetworkModule;
|
import org.briarproject.bramble.network.AndroidNetworkModule;
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
|
||||||
import org.briarproject.bramble.reporting.ReportingModule;
|
import org.briarproject.bramble.reporting.ReportingModule;
|
||||||
import org.briarproject.bramble.socks.SocksModule;
|
import org.briarproject.bramble.socks.SocksModule;
|
||||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ import dagger.Module;
|
|||||||
AndroidSystemModule.class,
|
AndroidSystemModule.class,
|
||||||
AndroidTaskSchedulerModule.class,
|
AndroidTaskSchedulerModule.class,
|
||||||
AndroidWakefulIoExecutorModule.class,
|
AndroidWakefulIoExecutorModule.class,
|
||||||
|
AndroidWakeLockModule.class,
|
||||||
DefaultThreadFactoryModule.class,
|
DefaultThreadFactoryModule.class,
|
||||||
CircumventionModule.class,
|
CircumventionModule.class,
|
||||||
DnsModule.class,
|
DnsModule.class,
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface AndroidWakeLock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquires the wake lock. This has no effect if the wake lock has already
|
|
||||||
* been acquired.
|
|
||||||
*/
|
|
||||||
void acquire();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the wake lock. This has no effect if the wake lock has already
|
|
||||||
* been released.
|
|
||||||
*/
|
|
||||||
void release();
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface AndroidWakeLockManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a wake lock with the given tag. The tag is only used for
|
|
||||||
* logging; the underlying OS wake lock will use its own tag.
|
|
||||||
*/
|
|
||||||
AndroidWakeLock createWakeLock(String tag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the given task while holding a wake lock.
|
|
||||||
*/
|
|
||||||
void runWakefully(Runnable r, String tag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submits the given task to the given executor while holding a wake lock.
|
|
||||||
* The lock is released when the task completes, or if an exception is
|
|
||||||
* thrown while submitting or running the task.
|
|
||||||
*/
|
|
||||||
void executeWakefully(Runnable r, Executor executor, String tag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a dedicated thread to run the given task asynchronously. A wake
|
|
||||||
* lock is acquired before starting the thread and released when the task
|
|
||||||
* completes, or if an exception is thrown while starting the thread or
|
|
||||||
* running the task.
|
|
||||||
* <p>
|
|
||||||
* This method should only be used for lifecycle management tasks that
|
|
||||||
* can't be run on an executor.
|
|
||||||
*/
|
|
||||||
void executeWakefully(Runnable r, String tag);
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,10 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
@@ -13,7 +14,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package org.briarproject.bramble.plugin.bluetooth;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLock;
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -33,8 +33,10 @@ class AndroidBluetoothTransportConnection
|
|||||||
super(plugin);
|
super(plugin);
|
||||||
this.connectionLimiter = connectionLimiter;
|
this.connectionLimiter = connectionLimiter;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
in = timeoutMonitor.createTimeoutInputStream(
|
InputStream socketIn = socket.getInputStream();
|
||||||
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
|
if (socketIn == null) throw new IOException();
|
||||||
|
in = timeoutMonitor.createTimeoutInputStream(socketIn,
|
||||||
|
plugin.getMaxIdleTime() * 2L);
|
||||||
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
|
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
|
||||||
wakeLock.acquire();
|
wakeLock.acquire();
|
||||||
String address = socket.getRemoteDevice().getAddress();
|
String address = socket.getRemoteDevice().getAddress();
|
||||||
@@ -48,7 +50,9 @@ class AndroidBluetoothTransportConnection
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OutputStream getOutputStream() throws IOException {
|
protected OutputStream getOutputStream() throws IOException {
|
||||||
return socket.getOutputStream();
|
OutputStream socketOut = socket.getOutputStream();
|
||||||
|
if (socketOut == null) throw new IOException();
|
||||||
|
return socketOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import androidx.annotation.ChecksSdkIntAtLeast;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
|
||||||
@ParametersNotNullByDefault
|
|
||||||
class AndroidTorPlugin extends TorPlugin {
|
|
||||||
|
|
||||||
private static final List<String> LIBRARY_ARCHITECTURES =
|
|
||||||
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
|
|
||||||
|
|
||||||
private static final String TOR_LIB_NAME = "libtor.so";
|
|
||||||
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
|
||||||
private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so";
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(AndroidTorPlugin.class.getName());
|
|
||||||
|
|
||||||
private final Application app;
|
|
||||||
private final AndroidWakeLock wakeLock;
|
|
||||||
private final File torLib, obfs4Lib, snowflakeLib;
|
|
||||||
|
|
||||||
AndroidTorPlugin(Executor ioExecutor,
|
|
||||||
Executor wakefulIoExecutor,
|
|
||||||
Application app,
|
|
||||||
NetworkManager networkManager,
|
|
||||||
LocationUtils locationUtils,
|
|
||||||
SocketFactory torSocketFactory,
|
|
||||||
Clock clock,
|
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
|
||||||
BatteryManager batteryManager,
|
|
||||||
AndroidWakeLockManager wakeLockManager,
|
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
|
||||||
PluginCallback callback,
|
|
||||||
String architecture,
|
|
||||||
long maxLatency,
|
|
||||||
int maxIdleTime,
|
|
||||||
File torDirectory,
|
|
||||||
int torSocksPort,
|
|
||||||
int torControlPort) {
|
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
|
||||||
torSocketFactory, clock, resourceProvider,
|
|
||||||
circumventionProvider, batteryManager, backoff,
|
|
||||||
torRendezvousCrypto, callback, architecture, maxLatency,
|
|
||||||
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
|
||||||
this.app = app;
|
|
||||||
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
|
||||||
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
|
||||||
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
|
||||||
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
|
||||||
snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getProcessId() {
|
|
||||||
return android.os.Process.myPid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getLastUpdateTime() {
|
|
||||||
try {
|
|
||||||
PackageManager pm = app.getPackageManager();
|
|
||||||
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0);
|
|
||||||
return pi.lastUpdateTime;
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void enableNetwork(boolean enable) throws IOException {
|
|
||||||
if (enable) wakeLock.acquire();
|
|
||||||
super.enableNetwork(enable);
|
|
||||||
if (!enable) wakeLock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ChecksSdkIntAtLeast(api = 25)
|
|
||||||
protected boolean canVerifyLetsEncryptCerts() {
|
|
||||||
return SDK_INT >= 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
super.stop();
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected File getTorExecutableFile() {
|
|
||||||
return torLib.exists() ? torLib : super.getTorExecutableFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected File getObfs4ExecutableFile() {
|
|
||||||
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected File getSnowflakeExecutableFile() {
|
|
||||||
return snowflakeLib.exists()
|
|
||||||
? snowflakeLib : super.getSnowflakeExecutableFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installTorExecutable() throws IOException {
|
|
||||||
installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installObfs4Executable() throws IOException {
|
|
||||||
installExecutable(super.getObfs4ExecutableFile(), obfs4Lib,
|
|
||||||
OBFS4_LIB_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void installSnowflakeExecutable() throws IOException {
|
|
||||||
installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib,
|
|
||||||
SNOWFLAKE_LIB_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void installExecutable(File extracted, File lib, String libName)
|
|
||||||
throws IOException {
|
|
||||||
if (lib.exists()) {
|
|
||||||
// If an older version left behind a binary, delete it
|
|
||||||
if (extracted.exists()) {
|
|
||||||
if (extracted.delete()) LOG.info("Deleted old binary");
|
|
||||||
else LOG.info("Failed to delete old binary");
|
|
||||||
}
|
|
||||||
} else if (SDK_INT < 29) {
|
|
||||||
// The binary wasn't extracted at install time. Try to extract it
|
|
||||||
extractLibraryFromApk(libName, extracted);
|
|
||||||
} else {
|
|
||||||
// No point extracting the binary, we won't be allowed to execute it
|
|
||||||
throw new FileNotFoundException(lib.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void extractLibraryFromApk(String libName, File dest)
|
|
||||||
throws IOException {
|
|
||||||
File sourceDir = new File(app.getApplicationInfo().sourceDir);
|
|
||||||
if (sourceDir.isFile()) {
|
|
||||||
// Look for other APK files in the same directory, if we're allowed
|
|
||||||
File parent = sourceDir.getParentFile();
|
|
||||||
if (parent != null) sourceDir = parent;
|
|
||||||
}
|
|
||||||
List<String> libPaths = getSupportedLibraryPaths(libName);
|
|
||||||
for (File apk : findApkFiles(sourceDir)) {
|
|
||||||
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
|
|
||||||
for (ZipEntry e = zin.getNextEntry(); e != null;
|
|
||||||
e = zin.getNextEntry()) {
|
|
||||||
if (libPaths.contains(e.getName())) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Extracting " + e.getName()
|
|
||||||
+ " from " + apk.getAbsolutePath());
|
|
||||||
}
|
|
||||||
extract(zin, dest); // Zip input stream will be closed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zin.close();
|
|
||||||
}
|
|
||||||
throw new FileNotFoundException(libName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all files with the extension .apk or .APK under the given root.
|
|
||||||
*/
|
|
||||||
private List<File> findApkFiles(File root) {
|
|
||||||
List<File> files = new ArrayList<>();
|
|
||||||
findApkFiles(root, files);
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findApkFiles(File f, List<File> files) {
|
|
||||||
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
|
|
||||||
files.add(f);
|
|
||||||
} else if (f.isDirectory()) {
|
|
||||||
File[] children = f.listFiles();
|
|
||||||
if (children != null) {
|
|
||||||
for (File child : children) findApkFiles(child, files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the paths at which libraries with the given name would be found
|
|
||||||
* inside an APK file, for all architectures supported by the device, in
|
|
||||||
* order of preference.
|
|
||||||
*/
|
|
||||||
private List<String> getSupportedLibraryPaths(String libName) {
|
|
||||||
List<String> architectures = new ArrayList<>();
|
|
||||||
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
|
||||||
if (LIBRARY_ARCHITECTURES.contains(abi)) {
|
|
||||||
architectures.add("lib/" + abi + "/" + libName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return architectures;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,11 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
@@ -13,12 +15,13 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
|
|||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.onionwrapper.AndroidTorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -28,6 +31,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedArchitectures;
|
import static org.briarproject.bramble.util.AndroidUtils.getSupportedArchitectures;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -39,13 +43,13 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@EventExecutor Executor eventExecutor,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
BackoffFactory backoffFactory,
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
@@ -55,8 +59,8 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
@TorControlPort int torControlPort,
|
@TorControlPort int torControlPort,
|
||||||
Application app,
|
Application app,
|
||||||
AndroidWakeLockManager wakeLockManager) {
|
AndroidWakeLockManager wakeLockManager) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@@ -79,12 +83,18 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(Backoff backoff,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new AndroidTorPlugin(ioExecutor,
|
TorWrapper tor = new AndroidTorWrapper(app, wakeLockManager,
|
||||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
ioExecutor, eventExecutor, architecture, torDirectory,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocksPort, torControlPort);
|
||||||
circumventionProvider, batteryManager, wakeLockManager,
|
// Android versions 7.1 and newer can verify Let's Encrypt TLS certs
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
// signed with the IdentTrust DST Root X3 certificate. Older versions
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
// of Android consider the certificate to have expired at the end of
|
||||||
torControlPort);
|
// September 2021.
|
||||||
|
boolean canVerifyLetsEncryptCerts = SDK_INT >= 25;
|
||||||
|
return new TorPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
|
networkManager, locationUtils, torSocketFactory,
|
||||||
|
circumventionProvider, batteryManager, backoff,
|
||||||
|
torRendezvousCrypto, tor, callback, MAX_LATENCY,
|
||||||
|
MAX_IDLE_TIME, canVerifyLetsEncryptCerts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.system;
|
|||||||
import org.briarproject.bramble.api.event.EventExecutor;
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
@@ -69,11 +68,4 @@ public class AndroidSystemModule {
|
|||||||
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
|
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
AndroidWakeLockManager provideWakeLockManager(
|
|
||||||
AndroidWakeLockManagerImpl wakeLockManager) {
|
|
||||||
return wakeLockManager;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import android.content.Intent;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
import org.briarproject.bramble.api.Cancellable;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.system.AlarmListener;
|
import org.briarproject.bramble.api.system.AlarmListener;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
import org.briarproject.bramble.api.system.Wakeful;
|
import org.briarproject.bramble.api.system.Wakeful;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package org.briarproject.bramble.system;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.AlarmListener;
|
import org.briarproject.bramble.api.system.AlarmListener;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.FINE;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around a {@link SharedWakeLock} that provides the more convenient
|
|
||||||
* semantics of {@link AndroidWakeLock} (i.e. calls to acquire() and release()
|
|
||||||
* don't need to be balanced).
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidWakeLockImpl implements AndroidWakeLock {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(AndroidWakeLockImpl.class.getName());
|
|
||||||
|
|
||||||
private static final AtomicInteger INSTANCE_ID = new AtomicInteger(0);
|
|
||||||
|
|
||||||
private final SharedWakeLock sharedWakeLock;
|
|
||||||
private final String tag;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean held = false;
|
|
||||||
|
|
||||||
AndroidWakeLockImpl(SharedWakeLock sharedWakeLock, String tag) {
|
|
||||||
this.sharedWakeLock = sharedWakeLock;
|
|
||||||
this.tag = tag + "_" + INSTANCE_ID.getAndIncrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void acquire() {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (held) {
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine(tag + " already acquired");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine(tag + " acquiring shared wake lock");
|
|
||||||
}
|
|
||||||
held = true;
|
|
||||||
sharedWakeLock.acquire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void release() {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (held) {
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine(tag + " releasing shared wake lock");
|
|
||||||
}
|
|
||||||
held = false;
|
|
||||||
sharedWakeLock.release();
|
|
||||||
} else {
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine(tag + " already released");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.content.Context.POWER_SERVICE;
|
|
||||||
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidWakeLockManagerImpl implements AndroidWakeLockManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How often to replace the wake lock.
|
|
||||||
*/
|
|
||||||
private static final long LOCK_DURATION_MS = MINUTES.toMillis(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically release the lock this many milliseconds after it's due
|
|
||||||
* to have been replaced and released.
|
|
||||||
*/
|
|
||||||
private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(30);
|
|
||||||
|
|
||||||
private final SharedWakeLock sharedWakeLock;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AndroidWakeLockManagerImpl(Application app,
|
|
||||||
ScheduledExecutorService scheduledExecutorService) {
|
|
||||||
PowerManager powerManager = (PowerManager)
|
|
||||||
requireNonNull(app.getSystemService(POWER_SERVICE));
|
|
||||||
String tag = getWakeLockTag(app);
|
|
||||||
sharedWakeLock = new RenewableWakeLock(powerManager,
|
|
||||||
scheduledExecutorService, PARTIAL_WAKE_LOCK, tag,
|
|
||||||
LOCK_DURATION_MS, SAFETY_MARGIN_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AndroidWakeLock createWakeLock(String tag) {
|
|
||||||
return new AndroidWakeLockImpl(sharedWakeLock, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runWakefully(Runnable r, String tag) {
|
|
||||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
|
||||||
wakeLock.acquire();
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void executeWakefully(Runnable r, Executor executor, String tag) {
|
|
||||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
|
||||||
wakeLock.acquire();
|
|
||||||
try {
|
|
||||||
executor.execute(() -> {
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
// Release the wake lock if the task throws an exception
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Release the wake lock if the executor throws an exception when
|
|
||||||
// we submit the task (in which case the release() call above won't
|
|
||||||
// happen)
|
|
||||||
wakeLock.release();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void executeWakefully(Runnable r, String tag) {
|
|
||||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
|
||||||
wakeLock.acquire();
|
|
||||||
try {
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
wakeLock.release();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getWakeLockTag(Context ctx) {
|
|
||||||
PackageManager pm = ctx.getPackageManager();
|
|
||||||
if (isInstalled(pm, "com.huawei.powergenie")) {
|
|
||||||
return "LocationManagerService";
|
|
||||||
} else if (isInstalled(pm, "com.evenwell.PowerMonitor")) {
|
|
||||||
return "AudioIn";
|
|
||||||
}
|
|
||||||
return ctx.getPackageName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInstalled(PackageManager pm, String packageName) {
|
|
||||||
try {
|
|
||||||
pm.getPackageInfo(packageName, 0);
|
|
||||||
return true;
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.PowerManager.WakeLock;
|
|
||||||
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.logging.Level.FINE;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class RenewableWakeLock implements SharedWakeLock {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(RenewableWakeLock.class.getName());
|
|
||||||
|
|
||||||
private final PowerManager powerManager;
|
|
||||||
private final ScheduledExecutorService scheduledExecutorService;
|
|
||||||
private final int levelAndFlags;
|
|
||||||
private final String tag;
|
|
||||||
private final long durationMs, safetyMarginMs;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private WakeLock wakeLock;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Future<?> future;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private int refCount = 0;
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private long acquired = 0;
|
|
||||||
|
|
||||||
RenewableWakeLock(PowerManager powerManager,
|
|
||||||
ScheduledExecutorService scheduledExecutorService,
|
|
||||||
int levelAndFlags,
|
|
||||||
String tag,
|
|
||||||
long durationMs,
|
|
||||||
long safetyMarginMs) {
|
|
||||||
this.powerManager = powerManager;
|
|
||||||
this.scheduledExecutorService = scheduledExecutorService;
|
|
||||||
this.levelAndFlags = levelAndFlags;
|
|
||||||
this.tag = tag;
|
|
||||||
this.durationMs = durationMs;
|
|
||||||
this.safetyMarginMs = safetyMarginMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void acquire() {
|
|
||||||
synchronized (lock) {
|
|
||||||
refCount++;
|
|
||||||
if (refCount == 1) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Acquiring wake lock " + tag);
|
|
||||||
}
|
|
||||||
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
|
|
||||||
// We do our own reference counting so we can replace the lock
|
|
||||||
// TODO: Check whether using a ref-counted wake lock affects
|
|
||||||
// power management apps
|
|
||||||
wakeLock.setReferenceCounted(false);
|
|
||||||
wakeLock.acquire(durationMs + safetyMarginMs);
|
|
||||||
future = scheduledExecutorService.schedule(this::renew,
|
|
||||||
durationMs, MILLISECONDS);
|
|
||||||
acquired = android.os.SystemClock.elapsedRealtime();
|
|
||||||
} else if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renew() {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
|
|
||||||
synchronized (lock) {
|
|
||||||
if (wakeLock == null) {
|
|
||||||
LOG.info("Already released");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
|
||||||
}
|
|
||||||
long now = android.os.SystemClock.elapsedRealtime();
|
|
||||||
long expiry = acquired + durationMs + safetyMarginMs;
|
|
||||||
if (now > expiry && LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.warning("Wake lock expired " + (now - expiry) + " ms ago");
|
|
||||||
}
|
|
||||||
WakeLock oldWakeLock = wakeLock;
|
|
||||||
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
|
|
||||||
wakeLock.setReferenceCounted(false);
|
|
||||||
wakeLock.acquire(durationMs + safetyMarginMs);
|
|
||||||
oldWakeLock.release();
|
|
||||||
future = scheduledExecutorService.schedule(this::renew, durationMs,
|
|
||||||
MILLISECONDS);
|
|
||||||
acquired = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void release() {
|
|
||||||
synchronized (lock) {
|
|
||||||
refCount--;
|
|
||||||
if (refCount == 0) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Releasing wake lock " + tag);
|
|
||||||
}
|
|
||||||
requireNonNull(future).cancel(false);
|
|
||||||
future = null;
|
|
||||||
requireNonNull(wakeLock).release();
|
|
||||||
wakeLock = null;
|
|
||||||
acquired = 0;
|
|
||||||
} else if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
interface SharedWakeLock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquires the wake lock. This increments the wake lock's reference count,
|
|
||||||
* so unlike {@link AndroidWakeLock#acquire()} every call to this method
|
|
||||||
* must be followed by a balancing call to {@link #release()}.
|
|
||||||
*/
|
|
||||||
void acquire();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the wake lock. This decrements the wake lock's reference count,
|
|
||||||
* so unlike {@link AndroidWakeLock#release()} every call to this method
|
|
||||||
* must follow a balancing call to {@link #acquire()}.
|
|
||||||
*/
|
|
||||||
void release();
|
|
||||||
}
|
|
||||||
@@ -24,9 +24,14 @@ dependencyVerification {
|
|||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
|
'org.briarproject:dont-kill-me-lib:0.2.6:dont-kill-me-lib-0.2.6.aar:8a4cc201143227c0865c2edfba035f71109bf02e1ab26444fa3e42d3c569960f',
|
||||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:tor-android:0.4.7.13:tor-android-0.4.7.13.jar:7852aab7d2298b80878c7719f34ce665725b494d673ecf2e6f9e697564638cc6',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
|
'org.briarproject:obfs4proxy-android:0.0.14-tor2:obfs4proxy-android-0.0.14-tor2.jar:a0a93770d6760ce57d9dbd31cc7177687374e00c3361dac22ab75e3b6e0f289e',
|
||||||
|
'org.briarproject:onionwrapper-android:0.0.1:onionwrapper-android-0.0.1.aar:959115946586daa090f057645cf75992407a59025e221c3bf88d2aa930ef3919',
|
||||||
|
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||||
|
'org.briarproject:snowflake-android:2.5.1:snowflake-android-2.5.1.jar:88ec81c17b1b6fa884d06839dec0330e328b45c89f88c970a213ce91ca8eac87',
|
||||||
|
'org.briarproject:tor-android:0.4.7.13-2:tor-android-0.4.7.13-2.jar:453fd463b234a2104edd7f0d02d0649cbb5c5efbe47a76df3828f55a3f90f8b5',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
@@ -37,11 +42,12 @@ dependencyVerification {
|
|||||||
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
||||||
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10:kotlin-stdlib-common-1.7.10.jar:19f102efe9629f8eabc63853ad15c533e47c47f91fca09285c5bde86e59f91d4',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0:kotlin-stdlib-common-1.8.0.jar:78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||||
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0:kotlin-stdlib-jdk7-1.8.0.jar:4c889d1d9803f5f2eb6c1592a6b7e62369ac7660c9eee15aba16fec059163666',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.8.0:kotlin-stdlib-1.8.0.jar:c77bef8774640b9fb9d6e217459ff220dae59878beb7d2e4b430506feffc654e',
|
||||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ public interface FeatureFlags {
|
|||||||
|
|
||||||
boolean shouldEnableDisappearingMessages();
|
boolean shouldEnableDisappearingMessages();
|
||||||
|
|
||||||
boolean shouldEnableMailbox();
|
|
||||||
|
|
||||||
boolean shouldEnablePrivateGroupsInCore();
|
boolean shouldEnablePrivateGroupsInCore();
|
||||||
|
|
||||||
boolean shouldEnableForumsInCore();
|
boolean shouldEnableForumsInCore();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -23,17 +24,24 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
|
|||||||
public abstract class BdfMessageValidator implements MessageValidator {
|
public abstract class BdfMessageValidator implements MessageValidator {
|
||||||
|
|
||||||
protected static final Logger LOG =
|
protected static final Logger LOG =
|
||||||
Logger.getLogger(BdfMessageValidator.class.getName());
|
getLogger(BdfMessageValidator.class.getName());
|
||||||
|
|
||||||
protected final ClientHelper clientHelper;
|
protected final ClientHelper clientHelper;
|
||||||
protected final MetadataEncoder metadataEncoder;
|
protected final MetadataEncoder metadataEncoder;
|
||||||
protected final Clock clock;
|
protected final Clock clock;
|
||||||
|
protected final boolean canonical;
|
||||||
|
|
||||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||||
MetadataEncoder metadataEncoder, Clock clock) {
|
MetadataEncoder metadataEncoder, Clock clock, boolean canonical) {
|
||||||
this.clientHelper = clientHelper;
|
this.clientHelper = clientHelper;
|
||||||
this.metadataEncoder = metadataEncoder;
|
this.metadataEncoder = metadataEncoder;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
|
this.canonical = canonical;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||||
|
MetadataEncoder metadataEncoder, Clock clock) {
|
||||||
|
this(clientHelper, metadataEncoder, clock, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract BdfMessageContext validateMessage(Message m, Group g,
|
protected abstract BdfMessageContext validateMessage(Message m, Group g,
|
||||||
@@ -49,7 +57,7 @@ public abstract class BdfMessageValidator implements MessageValidator {
|
|||||||
"Timestamp is too far in the future");
|
"Timestamp is too far in the future");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
BdfList bodyList = clientHelper.toList(m.getBody());
|
BdfList bodyList = clientHelper.toList(m, canonical);
|
||||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||||
return new MessageContext(meta, result.getDependencies());
|
return new MessageContext(meta, result.getDependencies());
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public interface ClientHelper {
|
|||||||
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
||||||
FormatException;
|
FormatException;
|
||||||
|
|
||||||
|
BdfList getMessageAsList(Transaction txn, MessageId m, boolean canonical)
|
||||||
|
throws DbException, FormatException;
|
||||||
|
|
||||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
||||||
FormatException;
|
FormatException;
|
||||||
|
|
||||||
@@ -106,6 +109,8 @@ public interface ClientHelper {
|
|||||||
|
|
||||||
BdfList toList(Message m) throws FormatException;
|
BdfList toList(Message m) throws FormatException;
|
||||||
|
|
||||||
|
BdfList toList(Message m, boolean canonical) throws FormatException;
|
||||||
|
|
||||||
BdfList toList(Author a);
|
BdfList toList(Author a);
|
||||||
|
|
||||||
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public class BdfDictionary extends TreeMap<String, Object> {
|
public final class BdfDictionary extends TreeMap<String, Object> {
|
||||||
|
|
||||||
public static final Object NULL_VALUE = new Object();
|
public static final Object NULL_VALUE = new Object();
|
||||||
|
|
||||||
@@ -39,9 +39,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getBoolean(String key) throws FormatException {
|
public Boolean getBoolean(String key) throws FormatException {
|
||||||
Object o = get(key);
|
Boolean value = getOptionalBoolean(key);
|
||||||
if (o instanceof Boolean) return (Boolean) o;
|
if (value == null) throw new FormatException();
|
||||||
throw new FormatException();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -52,19 +52,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
public Boolean getBoolean(String key, Boolean defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof Boolean) return (Boolean) o;
|
Boolean value = getOptionalBoolean(key);
|
||||||
return defaultValue;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getLong(String key) throws FormatException {
|
public Long getLong(String key) throws FormatException {
|
||||||
Object o = get(key);
|
Long value = getOptionalLong(key);
|
||||||
if (o instanceof Long) return (Long) o;
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
return value;
|
||||||
if (o instanceof Short) return ((Short) o).longValue();
|
|
||||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -78,20 +75,37 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getLong(String key, Long defaultValue) {
|
public Long getLong(String key, Long defaultValue) throws FormatException {
|
||||||
Object o = get(key);
|
Long value = getOptionalLong(key);
|
||||||
if (o instanceof Long) return (Long) o;
|
return value == null ? defaultValue : value;
|
||||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
}
|
||||||
if (o instanceof Short) return ((Short) o).longValue();
|
|
||||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
public Integer getInt(String key) throws FormatException {
|
||||||
return defaultValue;
|
Integer value = getOptionalInt(key);
|
||||||
|
if (value == null) throw new FormatException();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getOptionalInt(String key) throws FormatException {
|
||||||
|
Long value = getOptionalLong(key);
|
||||||
|
if (value == null) return null;
|
||||||
|
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
return value.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInt(String key, Integer defaultValue)
|
||||||
|
throws FormatException {
|
||||||
|
Integer value = getOptionalInt(key);
|
||||||
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getDouble(String key) throws FormatException {
|
public Double getDouble(String key) throws FormatException {
|
||||||
Object o = get(key);
|
Double value = getOptionalDouble(key);
|
||||||
if (o instanceof Double) return (Double) o;
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -103,17 +117,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getDouble(String key, Double defaultValue) {
|
public Double getDouble(String key, Double defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof Double) return (Double) o;
|
Double value = getOptionalDouble(key);
|
||||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String key) throws FormatException {
|
public String getString(String key) throws FormatException {
|
||||||
Object o = get(key);
|
String value = getOptionalString(key);
|
||||||
if (o instanceof String) return (String) o;
|
if (value == null) throw new FormatException();
|
||||||
throw new FormatException();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -124,17 +137,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String key, String defaultValue) {
|
public String getString(String key, String defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof String) return (String) o;
|
String value = getOptionalString(key);
|
||||||
return defaultValue;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRaw(String key) throws FormatException {
|
public byte[] getRaw(String key) throws FormatException {
|
||||||
Object o = get(key);
|
byte[] value = getOptionalRaw(key);
|
||||||
if (o instanceof byte[]) return (byte[]) o;
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -146,17 +158,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRaw(String key, byte[] defaultValue) {
|
public byte[] getRaw(String key, byte[] defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof byte[]) return (byte[]) o;
|
byte[] value = getOptionalRaw(key);
|
||||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfList getList(String key) throws FormatException {
|
public BdfList getList(String key) throws FormatException {
|
||||||
Object o = get(key);
|
BdfList value = getOptionalList(key);
|
||||||
if (o instanceof BdfList) return (BdfList) o;
|
if (value == null) throw new FormatException();
|
||||||
throw new FormatException();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -167,16 +178,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfList getList(String key, BdfList defaultValue) {
|
public BdfList getList(String key, BdfList defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof BdfList) return (BdfList) o;
|
BdfList value = getOptionalList(key);
|
||||||
return defaultValue;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary getDictionary(String key) throws FormatException {
|
public BdfDictionary getDictionary(String key) throws FormatException {
|
||||||
Object o = get(key);
|
BdfDictionary value = getOptionalDictionary(key);
|
||||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
if (value == null) throw new FormatException();
|
||||||
throw new FormatException();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -188,9 +199,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue) {
|
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue)
|
||||||
Object o = get(key);
|
throws FormatException {
|
||||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
BdfDictionary value = getOptionalDictionary(key);
|
||||||
return defaultValue;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public class BdfList extends ArrayList<Object> {
|
public final class BdfList extends ArrayList<Object> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for constructing lists inline.
|
* Factory method for constructing lists inline.
|
||||||
@@ -33,15 +33,15 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
super(items);
|
super(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
private boolean isInRange(int index) {
|
private boolean isInRange(int index) {
|
||||||
return index >= 0 && index < size();
|
return index >= 0 && index < size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getBoolean(int index) throws FormatException {
|
public Boolean getBoolean(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
Boolean value = getOptionalBoolean(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Boolean) return (Boolean) o;
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -53,21 +53,16 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getBoolean(int index, Boolean defaultValue) {
|
public Boolean getBoolean(int index, Boolean defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
Boolean value = getOptionalBoolean(index);
|
||||||
if (o instanceof Boolean) return (Boolean) o;
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getLong(int index) throws FormatException {
|
public Long getLong(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
Long value = getOptionalLong(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Long) return (Long) o;
|
return value;
|
||||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
|
||||||
if (o instanceof Short) return ((Short) o).longValue();
|
|
||||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -82,22 +77,37 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getLong(int index, Long defaultValue) {
|
public Long getLong(int index, Long defaultValue) throws FormatException {
|
||||||
if (!isInRange(index)) return defaultValue;
|
Long value = getOptionalLong(index);
|
||||||
Object o = get(index);
|
return value == null ? defaultValue : value;
|
||||||
if (o instanceof Long) return (Long) o;
|
}
|
||||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
|
||||||
if (o instanceof Short) return ((Short) o).longValue();
|
public Integer getInt(int index) throws FormatException {
|
||||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
Integer value = getOptionalInt(index);
|
||||||
return defaultValue;
|
if (value == null) throw new FormatException();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getOptionalInt(int index) throws FormatException {
|
||||||
|
Long value = getOptionalLong(index);
|
||||||
|
if (value == null) return null;
|
||||||
|
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
return value.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInt(int index, Integer defaultValue)
|
||||||
|
throws FormatException {
|
||||||
|
Integer value = getOptionalInt(index);
|
||||||
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getDouble(int index) throws FormatException {
|
public Double getDouble(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
Double value = getOptionalDouble(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof Double) return (Double) o;
|
return value;
|
||||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -110,19 +120,16 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getDouble(int index, Double defaultValue) {
|
public Double getDouble(int index, Double defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
Double value = getOptionalDouble(index);
|
||||||
if (o instanceof Double) return (Double) o;
|
return value == null ? defaultValue : value;
|
||||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(int index) throws FormatException {
|
public String getString(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
String value = getOptionalString(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof String) return (String) o;
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -134,19 +141,16 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(int index, String defaultValue) {
|
public String getString(int index, String defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
String value = getOptionalString(index);
|
||||||
if (o instanceof String) return (String) o;
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRaw(int index) throws FormatException {
|
public byte[] getRaw(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
byte[] value = getOptionalRaw(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof byte[]) return (byte[]) o;
|
return value;
|
||||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -159,19 +163,16 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRaw(int index, byte[] defaultValue) {
|
public byte[] getRaw(int index, byte[] defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
byte[] value = getOptionalRaw(index);
|
||||||
if (o instanceof byte[]) return (byte[]) o;
|
return value == null ? defaultValue : value;
|
||||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfList getList(int index) throws FormatException {
|
public BdfList getList(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
BdfList value = getOptionalList(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof BdfList) return (BdfList) o;
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -183,18 +184,16 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfList getList(int index, BdfList defaultValue) {
|
public BdfList getList(int index, BdfList defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
BdfList value = getOptionalList(index);
|
||||||
if (o instanceof BdfList) return (BdfList) o;
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary getDictionary(int index) throws FormatException {
|
public BdfDictionary getDictionary(int index) throws FormatException {
|
||||||
if (!isInRange(index)) throw new FormatException();
|
BdfDictionary value = getOptionalDictionary(index);
|
||||||
Object o = get(index);
|
if (value == null) throw new FormatException();
|
||||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
return value;
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -207,10 +206,9 @@ public class BdfList extends ArrayList<Object> {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue) {
|
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue)
|
||||||
if (!isInRange(index)) return defaultValue;
|
throws FormatException {
|
||||||
Object o = get(index);
|
BdfDictionary value = getOptionalDictionary(index);
|
||||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
return value == null ? defaultValue : value;
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ public interface BdfReader {
|
|||||||
|
|
||||||
void skipLong() throws IOException;
|
void skipLong() throws IOException;
|
||||||
|
|
||||||
|
boolean hasInt() throws IOException;
|
||||||
|
|
||||||
|
int readInt() throws IOException;
|
||||||
|
|
||||||
|
void skipInt() throws IOException;
|
||||||
|
|
||||||
boolean hasDouble() throws IOException;
|
boolean hasDouble() throws IOException;
|
||||||
|
|
||||||
double readDouble() throws IOException;
|
double readDouble() throws IOException;
|
||||||
@@ -54,23 +60,11 @@ public interface BdfReader {
|
|||||||
|
|
||||||
BdfList readList() throws IOException;
|
BdfList readList() throws IOException;
|
||||||
|
|
||||||
void readListStart() throws IOException;
|
|
||||||
|
|
||||||
boolean hasListEnd() throws IOException;
|
|
||||||
|
|
||||||
void readListEnd() throws IOException;
|
|
||||||
|
|
||||||
void skipList() throws IOException;
|
void skipList() throws IOException;
|
||||||
|
|
||||||
boolean hasDictionary() throws IOException;
|
boolean hasDictionary() throws IOException;
|
||||||
|
|
||||||
BdfDictionary readDictionary() throws IOException;
|
BdfDictionary readDictionary() throws IOException;
|
||||||
|
|
||||||
void readDictionaryStart() throws IOException;
|
|
||||||
|
|
||||||
boolean hasDictionaryEnd() throws IOException;
|
|
||||||
|
|
||||||
void readDictionaryEnd() throws IOException;
|
|
||||||
|
|
||||||
void skipDictionary() throws IOException;
|
void skipDictionary() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public interface BdfReaderFactory {
|
|||||||
|
|
||||||
BdfReader createReader(InputStream in);
|
BdfReader createReader(InputStream in);
|
||||||
|
|
||||||
|
BdfReader createReader(InputStream in, boolean canonical);
|
||||||
|
|
||||||
BdfReader createReader(InputStream in, int nestedLimit,
|
BdfReader createReader(InputStream in, int nestedLimit,
|
||||||
int maxBufferSize);
|
int maxBufferSize, boolean canonical);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,5 @@ public interface BdfWriter {
|
|||||||
|
|
||||||
void writeList(Collection<?> c) throws IOException;
|
void writeList(Collection<?> c) throws IOException;
|
||||||
|
|
||||||
void writeListStart() throws IOException;
|
|
||||||
|
|
||||||
void writeListEnd() throws IOException;
|
|
||||||
|
|
||||||
void writeDictionary(Map<?, ?> m) throws IOException;
|
void writeDictionary(Map<?, ?> m) throws IOException;
|
||||||
|
|
||||||
void writeDictionaryStart() throws IOException;
|
|
||||||
|
|
||||||
void writeDictionaryEnd() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
@@ -34,6 +35,16 @@ public interface MailboxManager {
|
|||||||
*/
|
*/
|
||||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
MailboxPairingTask startPairingTask(String qrCodePayload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a textual QR code representation in
|
||||||
|
* {@link org.briarproject.bramble.util.Base32} format and converts it
|
||||||
|
* into a qrCodePayload as expected by {@link #startPairingTask(String)}.
|
||||||
|
*
|
||||||
|
* @throws FormatException when the provided payload did not include a
|
||||||
|
* proper briar-mailbox:// link.
|
||||||
|
*/
|
||||||
|
String convertBase32Payload(String base32Payload) throws FormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be used by the UI to test the mailbox connection.
|
* Can be used by the UI to test the mailbox connection.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.startsWithIgnoreCase;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class OsUtils {
|
public class OsUtils {
|
||||||
|
|
||||||
@@ -13,15 +15,15 @@ public class OsUtils {
|
|||||||
private static final String vendor = System.getProperty("java.vendor");
|
private static final String vendor = System.getProperty("java.vendor");
|
||||||
|
|
||||||
public static boolean isWindows() {
|
public static boolean isWindows() {
|
||||||
return os != null && os.contains("Windows");
|
return os != null && startsWithIgnoreCase(os, "Win");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isMac() {
|
public static boolean isMac() {
|
||||||
return os != null && os.contains("Mac OS");
|
return os != null && os.equalsIgnoreCase("Mac OS X");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLinux() {
|
public static boolean isLinux() {
|
||||||
return os != null && os.contains("Linux") && !isAndroid();
|
return os != null && startsWithIgnoreCase(os, "Linux") && !isAndroid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAndroid() {
|
public static boolean isAndroid() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.regex.Pattern;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.nio.charset.CodingErrorAction.IGNORE;
|
import static java.nio.charset.CodingErrorAction.IGNORE;
|
||||||
|
import static java.nio.charset.CodingErrorAction.REPORT;
|
||||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||||
|
|
||||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||||
@@ -52,26 +53,38 @@ public class StringUtils {
|
|||||||
return s.getBytes(UTF_8);
|
return s.getBytes(UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fromUtf8(byte[] bytes) {
|
public static String fromUtf8(byte[] bytes) throws FormatException {
|
||||||
return fromUtf8(bytes, 0, bytes.length);
|
return fromUtf8(bytes, 0, bytes.length, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fromUtf8(byte[] bytes, int off, int len) {
|
public static String fromUtf8(byte[] bytes, int off, int len)
|
||||||
|
throws FormatException {
|
||||||
|
return fromUtf8(bytes, off, len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fromUtf8(byte[] bytes, int off, int len,
|
||||||
|
boolean strict) throws FormatException {
|
||||||
CharsetDecoder decoder = UTF_8.newDecoder();
|
CharsetDecoder decoder = UTF_8.newDecoder();
|
||||||
decoder.onMalformedInput(IGNORE);
|
decoder.onMalformedInput(strict ? REPORT : IGNORE);
|
||||||
decoder.onUnmappableCharacter(IGNORE);
|
decoder.onUnmappableCharacter(strict ? REPORT : IGNORE);
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(bytes, off, len);
|
ByteBuffer buffer = ByteBuffer.wrap(bytes, off, len);
|
||||||
try {
|
try {
|
||||||
return decoder.decode(buffer).toString();
|
return decoder.decode(buffer).toString();
|
||||||
} catch (CharacterCodingException e) {
|
} catch (CharacterCodingException e) {
|
||||||
throw new AssertionError(e);
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String truncateUtf8(String s, int maxUtf8Length) {
|
public static String truncateUtf8(String s, int maxUtf8Length) {
|
||||||
byte[] utf8 = toUtf8(s);
|
byte[] utf8 = toUtf8(s);
|
||||||
if (utf8.length <= maxUtf8Length) return s;
|
if (utf8.length <= maxUtf8Length) return s;
|
||||||
return fromUtf8(utf8, 0, maxUtf8Length);
|
// Don't be strict when converting back, so that if we truncate a
|
||||||
|
// multi-byte character the whole character gets dropped
|
||||||
|
try {
|
||||||
|
return fromUtf8(utf8, 0, maxUtf8Length, false);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,4 +176,9 @@ public class StringUtils {
|
|||||||
}
|
}
|
||||||
return new String(c);
|
return new String(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see https://stackoverflow.com/a/38947571
|
||||||
|
static boolean startsWithIgnoreCase(String s, String prefix) {
|
||||||
|
return s.regionMatches(true, 0, prefix, 0, prefix.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.data;
|
package org.briarproject.bramble.api.data;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -8,9 +9,12 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import static java.lang.Boolean.TRUE;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class BdfDictionaryTest extends BrambleTestCase {
|
public class BdfDictionaryTest extends BrambleTestCase {
|
||||||
@@ -19,20 +23,20 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
public void testConstructors() {
|
public void testConstructors() {
|
||||||
assertEquals(Collections.<String, Object>emptyMap(),
|
assertEquals(Collections.<String, Object>emptyMap(),
|
||||||
new BdfDictionary());
|
new BdfDictionary());
|
||||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||||
new BdfDictionary(Collections.singletonMap("foo", NULL_VALUE)));
|
new BdfDictionary(singletonMap("foo", NULL_VALUE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFactoryMethod() {
|
public void testFactoryMethod() {
|
||||||
assertEquals(Collections.<String, Object>emptyMap(),
|
assertEquals(Collections.<String, Object>emptyMap(),
|
||||||
BdfDictionary.of());
|
BdfDictionary.of());
|
||||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)));
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntegerPromotion() throws Exception {
|
public void testLongPromotion() throws Exception {
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
d.put("foo", (byte) 1);
|
d.put("foo", (byte) 1);
|
||||||
d.put("bar", (short) 2);
|
d.put("bar", (short) 2);
|
||||||
@@ -44,6 +48,33 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
assertEquals(Long.valueOf(4), d.getLong("bam"));
|
assertEquals(Long.valueOf(4), d.getLong("bam"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntPromotionAndDemotion() throws Exception {
|
||||||
|
BdfDictionary d = new BdfDictionary();
|
||||||
|
d.put("foo", (byte) 1);
|
||||||
|
d.put("bar", (short) 2);
|
||||||
|
d.put("baz", 3);
|
||||||
|
d.put("bam", 4L);
|
||||||
|
assertEquals(Integer.valueOf(1), d.getInt("foo"));
|
||||||
|
assertEquals(Integer.valueOf(2), d.getInt("bar"));
|
||||||
|
assertEquals(Integer.valueOf(3), d.getInt("baz"));
|
||||||
|
assertEquals(Integer.valueOf(4), d.getInt("bam"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testIntUnderflow() throws Exception {
|
||||||
|
BdfDictionary d =
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", Integer.MIN_VALUE - 1L));
|
||||||
|
d.getInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testIntOverflow() throws Exception {
|
||||||
|
BdfDictionary d =
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", Integer.MAX_VALUE + 1L));
|
||||||
|
d.getInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFloatPromotion() throws Exception {
|
public void testFloatPromotion() throws Exception {
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
@@ -67,7 +98,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeySetIteratorIsOrderedByKeys() throws Exception {
|
public void testKeySetIteratorIsOrderedByKeys() {
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
d.put("a", 1);
|
d.put("a", 1);
|
||||||
d.put("d", 4);
|
d.put("d", 4);
|
||||||
@@ -86,7 +117,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValuesIteratorIsOrderedByKeys() throws Exception {
|
public void testValuesIteratorIsOrderedByKeys() {
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
d.put("a", 1);
|
d.put("a", 1);
|
||||||
d.put("d", 4);
|
d.put("d", 4);
|
||||||
@@ -105,7 +136,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEntrySetIteratorIsOrderedByKeys() throws Exception {
|
public void testEntrySetIteratorIsOrderedByKeys() {
|
||||||
BdfDictionary d = new BdfDictionary();
|
BdfDictionary d = new BdfDictionary();
|
||||||
d.put("a", 1);
|
d.put("a", 1);
|
||||||
d.put("d", 4);
|
d.put("d", 4);
|
||||||
@@ -130,4 +161,284 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
|||||||
assertEquals("d", e.getKey());
|
assertEquals("d", e.getKey());
|
||||||
assertEquals(4, e.getValue());
|
assertEquals(4, e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getBoolean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getLong("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForIntThrowsFormatException() throws Exception {
|
||||||
|
new BdfDictionary().getInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getDouble("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getString("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForRawThrowsFormatException() throws Exception {
|
||||||
|
new BdfDictionary().getRaw("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getList("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testMissingValueForDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfDictionary().getDictionary("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getBoolean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getLong("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDouble("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getString("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getRaw("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getList("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDictionary("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalMethodsReturnNullForMissingValue()
|
||||||
|
throws Exception {
|
||||||
|
testOptionalMethodsReturnNull(new BdfDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||||
|
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||||
|
testOptionalMethodsReturnNull(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testOptionalMethodsReturnNull(BdfDictionary d)
|
||||||
|
throws Exception {
|
||||||
|
assertNull(d.getOptionalBoolean("foo"));
|
||||||
|
assertNull(d.getOptionalLong("foo"));
|
||||||
|
assertNull(d.getOptionalInt("foo"));
|
||||||
|
assertNull(d.getOptionalDouble("foo"));
|
||||||
|
assertNull(d.getOptionalString("foo"));
|
||||||
|
assertNull(d.getOptionalRaw("foo"));
|
||||||
|
assertNull(d.getOptionalList("foo"));
|
||||||
|
assertNull(d.getOptionalDictionary("foo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultMethodsReturnDefaultForMissingValue()
|
||||||
|
throws Exception {
|
||||||
|
testDefaultMethodsReturnDefault(new BdfDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||||
|
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||||
|
testDefaultMethodsReturnDefault(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testDefaultMethodsReturnDefault(BdfDictionary d)
|
||||||
|
throws Exception {
|
||||||
|
assertEquals(TRUE, d.getBoolean("foo", TRUE));
|
||||||
|
assertEquals(Long.valueOf(123L), d.getLong("foo", 123L));
|
||||||
|
assertEquals(Integer.valueOf(123), d.getInt("foo", 123));
|
||||||
|
assertEquals(Double.valueOf(123D), d.getDouble("foo", 123D));
|
||||||
|
assertEquals("123", d.getString("foo", "123"));
|
||||||
|
byte[] defaultRaw = {1, 2, 3};
|
||||||
|
assertArrayEquals(defaultRaw, d.getRaw("foo", defaultRaw));
|
||||||
|
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||||
|
assertEquals(defaultList, d.getList("foo", defaultList));
|
||||||
|
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||||
|
assertEquals(defaultDict, d.getDictionary("foo", defaultDict));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalBoolean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalLong("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo", 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalInt("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDouble("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo", 1D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalString("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalRawThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalRaw("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo", new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalList("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo",
|
||||||
|
new BdfList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDictionary("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo",
|
||||||
|
new BdfDictionary());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,31 +5,31 @@ import org.briarproject.bramble.api.FormatException;
|
|||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import static java.lang.Boolean.TRUE;
|
||||||
import java.util.Collections;
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
public class BdfListTest extends BrambleTestCase {
|
public class BdfListTest extends BrambleTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructors() {
|
public void testConstructors() {
|
||||||
assertEquals(Collections.emptyList(), new BdfList());
|
assertEquals(emptyList(), new BdfList());
|
||||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
assertEquals(asList(1, 2, NULL_VALUE),
|
||||||
new BdfList(Arrays.asList(1, 2, NULL_VALUE)));
|
new BdfList(asList(1, 2, NULL_VALUE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFactoryMethod() {
|
public void testFactoryMethod() {
|
||||||
assertEquals(Collections.emptyList(), BdfList.of());
|
assertEquals(emptyList(), BdfList.of());
|
||||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
assertEquals(asList(1, 2, NULL_VALUE), BdfList.of(1, 2, NULL_VALUE));
|
||||||
BdfList.of(1, 2, NULL_VALUE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntegerPromotion() throws Exception {
|
public void testLongPromotion() throws Exception {
|
||||||
BdfList list = new BdfList();
|
BdfList list = new BdfList();
|
||||||
list.add((byte) 1);
|
list.add((byte) 1);
|
||||||
list.add((short) 2);
|
list.add((short) 2);
|
||||||
@@ -41,6 +41,31 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
assertEquals(Long.valueOf(4), list.getLong(3));
|
assertEquals(Long.valueOf(4), list.getLong(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntPromotionAndDemotion() throws Exception {
|
||||||
|
BdfList list = new BdfList();
|
||||||
|
list.add((byte) 1);
|
||||||
|
list.add((short) 2);
|
||||||
|
list.add(3);
|
||||||
|
list.add(4L);
|
||||||
|
assertEquals(Integer.valueOf(1), list.getInt(0));
|
||||||
|
assertEquals(Integer.valueOf(2), list.getInt(1));
|
||||||
|
assertEquals(Integer.valueOf(3), list.getInt(2));
|
||||||
|
assertEquals(Integer.valueOf(4), list.getInt(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testIntUnderflow() throws Exception {
|
||||||
|
BdfList list = BdfList.of(Integer.MIN_VALUE - 1L);
|
||||||
|
list.getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testIntOverflow() throws Exception {
|
||||||
|
BdfList list = BdfList.of(Integer.MAX_VALUE + 1L);
|
||||||
|
list.getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFloatPromotion() throws Exception {
|
public void testFloatPromotion() throws Exception {
|
||||||
BdfList list = new BdfList();
|
BdfList list = new BdfList();
|
||||||
@@ -63,61 +88,6 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(new byte[123], second);
|
assertArrayEquals(new byte[123], second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
public void testIndexOutOfBoundsReturnsDefaultValue() throws Exception {
|
|
||||||
BdfList list = BdfList.of(1, 2, 3);
|
|
||||||
boolean defaultBoolean = true;
|
|
||||||
assertEquals(defaultBoolean, list.getBoolean(-1, defaultBoolean));
|
|
||||||
assertEquals(defaultBoolean, list.getBoolean(3, defaultBoolean));
|
|
||||||
Long defaultLong = 123L;
|
|
||||||
assertEquals(defaultLong, list.getLong(-1, defaultLong));
|
|
||||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
|
||||||
Double defaultDouble = 1.23;
|
|
||||||
assertEquals(defaultDouble, list.getDouble(-1, defaultDouble));
|
|
||||||
assertEquals(defaultDouble, list.getDouble(3, defaultDouble));
|
|
||||||
String defaultString = "123";
|
|
||||||
assertEquals(defaultString, list.getString(-1, defaultString));
|
|
||||||
assertEquals(defaultString, list.getString(3, defaultString));
|
|
||||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
|
||||||
assertArrayEquals(defaultBytes, list.getRaw(-1, defaultBytes));
|
|
||||||
assertArrayEquals(defaultBytes, list.getRaw(3, defaultBytes));
|
|
||||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
|
||||||
assertEquals(defaultList, list.getList(-1, defaultList));
|
|
||||||
assertEquals(defaultList, list.getList(3, defaultList));
|
|
||||||
BdfDictionary defaultDict = BdfDictionary.of(
|
|
||||||
new BdfEntry("1", 1),
|
|
||||||
new BdfEntry("2", 2),
|
|
||||||
new BdfEntry("3", 3)
|
|
||||||
);
|
|
||||||
assertEquals(defaultDict, list.getDictionary(-1, defaultDict));
|
|
||||||
assertEquals(defaultDict, list.getDictionary(3, defaultDict));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
public void testWrongTypeReturnsDefaultValue() throws Exception {
|
|
||||||
BdfList list = BdfList.of(1, 2, 3, true);
|
|
||||||
boolean defaultBoolean = true;
|
|
||||||
assertEquals(defaultBoolean, list.getBoolean(0, defaultBoolean));
|
|
||||||
Long defaultLong = 123L;
|
|
||||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
|
||||||
Double defaultDouble = 1.23;
|
|
||||||
assertEquals(defaultDouble, list.getDouble(0, defaultDouble));
|
|
||||||
String defaultString = "123";
|
|
||||||
assertEquals(defaultString, list.getString(0, defaultString));
|
|
||||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
|
||||||
assertArrayEquals(defaultBytes, list.getRaw(0, defaultBytes));
|
|
||||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
|
||||||
assertEquals(defaultList, list.getList(0, defaultList));
|
|
||||||
BdfDictionary defaultDict = BdfDictionary.of(
|
|
||||||
new BdfEntry("1", 1),
|
|
||||||
new BdfEntry("2", 2),
|
|
||||||
new BdfEntry("3", 3)
|
|
||||||
);
|
|
||||||
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForBooleanThrowsFormatException()
|
public void testNegativeIndexForBooleanThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -130,6 +100,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalBoolean(-1);
|
new BdfList().getOptionalBoolean(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getBoolean(-1, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForLongThrowsFormatException()
|
public void testNegativeIndexForLongThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -142,6 +118,30 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalLong(-1);
|
new BdfList().getOptionalLong(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getLong(-1, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getInt(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForOptionalIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getOptionalInt(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getInt(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForDoubleThrowsFormatException()
|
public void testNegativeIndexForDoubleThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -154,6 +154,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalDouble(-1);
|
new BdfList().getOptionalDouble(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getDouble(-1, 1D);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForStringThrowsFormatException()
|
public void testNegativeIndexForStringThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -166,6 +172,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalString(-1);
|
new BdfList().getOptionalString(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getString(-1, "");
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForRawThrowsFormatException()
|
public void testNegativeIndexForRawThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -178,6 +190,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalRaw(-1);
|
new BdfList().getOptionalRaw(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultRawThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getRaw(-1, new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForListThrowsFormatException()
|
public void testNegativeIndexForListThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -190,6 +208,11 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalList(-1);
|
new BdfList().getOptionalList(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getList(-1, new BdfList());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNegativeIndexForDictionaryThrowsFormatException()
|
public void testNegativeIndexForDictionaryThrowsFormatException()
|
||||||
@@ -203,6 +226,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalDictionary(-1);
|
new BdfList().getOptionalDictionary(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNegativeIndexForDefaultDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getDictionary(-1, new BdfDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForBooleanThrowsFormatException()
|
public void testTooLargeIndexForBooleanThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -215,6 +244,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalBoolean(0);
|
new BdfList().getOptionalBoolean(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getBoolean(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForLongThrowsFormatException()
|
public void testTooLargeIndexForLongThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -227,6 +262,30 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalLong(0);
|
new BdfList().getOptionalLong(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getLong(0, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForOptionalIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getOptionalInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getInt(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForDoubleThrowsFormatException()
|
public void testTooLargeIndexForDoubleThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -239,6 +298,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalDouble(0);
|
new BdfList().getOptionalDouble(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getDouble(0, 1D);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForStringThrowsFormatException()
|
public void testTooLargeIndexForStringThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -251,6 +316,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalString(0);
|
new BdfList().getOptionalString(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getString(0, "");
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForRawThrowsFormatException()
|
public void testTooLargeIndexForRawThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -263,6 +334,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalRaw(0);
|
new BdfList().getOptionalRaw(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultRawThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getRaw(0, new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForListThrowsFormatException()
|
public void testTooLargeIndexForListThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -275,6 +352,11 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
new BdfList().getOptionalList(0);
|
new BdfList().getOptionalList(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getList(0, new BdfList());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testTooLargeIndexForDictionaryThrowsFormatException()
|
public void testTooLargeIndexForDictionaryThrowsFormatException()
|
||||||
@@ -287,6 +369,13 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
new BdfList().getOptionalDictionary(0);
|
new BdfList().getOptionalDictionary(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testTooLargeIndexForDefaultDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
new BdfList().getDictionary(0, new BdfDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForBooleanThrowsFormatException()
|
public void testWrongTypeForBooleanThrowsFormatException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -299,6 +388,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(123).getOptionalBoolean(0);
|
BdfList.of(123).getOptionalBoolean(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getBoolean(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||||
BdfList.of(1.23).getLong(0);
|
BdfList.of(1.23).getLong(0);
|
||||||
@@ -310,6 +405,29 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(1.23).getOptionalLong(0);
|
BdfList.of(1.23).getOptionalLong(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(1.23).getLong(0, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(1.23).getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(1.23).getOptionalInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(1.23).getInt(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||||
BdfList.of(123).getDouble(0);
|
BdfList.of(123).getDouble(0);
|
||||||
@@ -321,6 +439,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(123).getOptionalDouble(0);
|
BdfList.of(123).getOptionalDouble(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getDouble(0, 1D);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||||
BdfList.of(123).getString(0);
|
BdfList.of(123).getString(0);
|
||||||
@@ -332,6 +456,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(123).getOptionalString(0);
|
BdfList.of(123).getOptionalString(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getString(0, "");
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||||
BdfList.of(123).getRaw(0);
|
BdfList.of(123).getRaw(0);
|
||||||
@@ -343,6 +473,12 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(123).getOptionalRaw(0);
|
BdfList.of(123).getOptionalRaw(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getRaw(0, new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForListThrowsFormatException() throws Exception {
|
public void testWrongTypeForListThrowsFormatException() throws Exception {
|
||||||
BdfList.of(123).getList(0);
|
BdfList.of(123).getList(0);
|
||||||
@@ -354,6 +490,11 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
BdfList.of(123).getOptionalList(0);
|
BdfList.of(123).getOptionalList(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getList(0, new BdfList());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||||
@@ -366,4 +507,81 @@ public class BdfListTest extends BrambleTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
BdfList.of(123).getOptionalDictionary(0);
|
BdfList.of(123).getOptionalDictionary(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(123).getDictionary(0, new BdfDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForBooleanThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getBoolean(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getLong(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getDouble(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getString(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getRaw(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getList(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testNullValueForDictionaryThrowsFormatException()
|
||||||
|
throws Exception {
|
||||||
|
BdfList.of(NULL_VALUE).getDictionary(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||||
|
BdfList list = BdfList.of(NULL_VALUE);
|
||||||
|
assertNull(list.getOptionalBoolean(0));
|
||||||
|
assertNull(list.getOptionalLong(0));
|
||||||
|
assertNull(list.getOptionalInt(0));
|
||||||
|
assertNull(list.getOptionalDouble(0));
|
||||||
|
assertNull(list.getOptionalString(0));
|
||||||
|
assertNull(list.getOptionalRaw(0));
|
||||||
|
assertNull(list.getOptionalList(0));
|
||||||
|
assertNull(list.getOptionalDictionary(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||||
|
BdfList list = BdfList.of(NULL_VALUE);
|
||||||
|
assertEquals(TRUE, list.getBoolean(0, TRUE));
|
||||||
|
assertEquals(Long.valueOf(123L), list.getLong(0, 123L));
|
||||||
|
assertEquals(Integer.valueOf(123), list.getInt(0, 123));
|
||||||
|
assertEquals(Double.valueOf(123D), list.getDouble(0, 123D));
|
||||||
|
assertEquals("123", list.getString(0, "123"));
|
||||||
|
byte[] defaultRaw = {1, 2, 3};
|
||||||
|
assertArrayEquals(defaultRaw, list.getRaw(0, defaultRaw));
|
||||||
|
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||||
|
assertEquals(defaultList, list.getList(0, defaultList));
|
||||||
|
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||||
|
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ apply from: '../dagger.gradle'
|
|||||||
dependencies {
|
dependencies {
|
||||||
api project(':bramble-api')
|
api project(':bramble-api')
|
||||||
|
|
||||||
api 'org.briarproject:jtorctl:0.5'
|
api "org.briarproject:onionwrapper-core:$onionwrapper_version"
|
||||||
|
|
||||||
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
|
|||||||
@@ -155,7 +155,13 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
@Override
|
@Override
|
||||||
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
return toList(db.getMessage(txn, m).getBody());
|
return getMessageAsList(txn, m, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BdfList getMessageAsList(Transaction txn, MessageId m,
|
||||||
|
boolean canonical) throws DbException, FormatException {
|
||||||
|
return toList(db.getMessage(txn, m), canonical);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -313,8 +319,13 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
||||||
|
return toList(b, off, len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfList toList(byte[] b, int off, int len, boolean canonical)
|
||||||
|
throws FormatException {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
||||||
BdfReader reader = bdfReaderFactory.createReader(in);
|
BdfReader reader = bdfReaderFactory.createReader(in, canonical);
|
||||||
try {
|
try {
|
||||||
BdfList list = reader.readList();
|
BdfList list = reader.readList();
|
||||||
if (!reader.eof()) throw new FormatException();
|
if (!reader.eof()) throw new FormatException();
|
||||||
@@ -328,7 +339,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BdfList toList(byte[] b) throws FormatException {
|
public BdfList toList(byte[] b) throws FormatException {
|
||||||
return toList(b, 0, b.length);
|
return toList(b, 0, b.length, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -336,6 +347,12 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
return toList(m.getBody());
|
return toList(m.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BdfList toList(Message m, boolean canonical) throws FormatException {
|
||||||
|
byte[] b = m.getBody();
|
||||||
|
return toList(b, 0, b.length, canonical);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BdfList toList(Author a) {
|
public BdfList toList(Author a) {
|
||||||
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
||||||
@@ -361,7 +378,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
public Author parseAndValidateAuthor(BdfList author)
|
public Author parseAndValidateAuthor(BdfList author)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
checkSize(author, 3);
|
checkSize(author, 3);
|
||||||
int formatVersion = author.getLong(0).intValue();
|
int formatVersion = author.getInt(0);
|
||||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||||
String name = author.getString(1);
|
String name = author.getString(1);
|
||||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||||
@@ -472,8 +489,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
if (element.size() != 2) {
|
if (element.size() != 2) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
list.add(new MailboxVersion(element.getInt(0), element.getInt(1)));
|
||||||
element.getLong(1).intValue()));
|
|
||||||
}
|
}
|
||||||
// Sort the list of versions for easier comparison
|
// Sort the list of versions for easier comparison
|
||||||
sort(list);
|
sort(list);
|
||||||
@@ -486,7 +502,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
try {
|
try {
|
||||||
BdfDictionary meta =
|
BdfDictionary meta =
|
||||||
getGroupMetadataAsDictionary(txn, contactGroupId);
|
getGroupMetadataAsDictionary(txn, contactGroupId);
|
||||||
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
|
return new ContactId(meta.getInt(GROUP_KEY_CONTACT_ID));
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e); // Invalid group metadata
|
throw new DbException(e); // Invalid group metadata
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,18 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
|
|||||||
@Override
|
@Override
|
||||||
public BdfReader createReader(InputStream in) {
|
public BdfReader createReader(InputStream in) {
|
||||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||||
DEFAULT_MAX_BUFFER_SIZE);
|
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BdfReader createReader(InputStream in, boolean canonical) {
|
||||||
|
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||||
|
DEFAULT_MAX_BUFFER_SIZE, canonical);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BdfReader createReader(InputStream in, int nestedLimit,
|
public BdfReader createReader(InputStream in, int nestedLimit,
|
||||||
int maxBufferSize) {
|
int maxBufferSize, boolean canonical) {
|
||||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
|
return new BdfReaderImpl(in, nestedLimit, maxBufferSize, canonical);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,21 +33,24 @@ import static org.briarproject.bramble.util.StringUtils.fromUtf8;
|
|||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class BdfReaderImpl implements BdfReader {
|
final class BdfReaderImpl implements BdfReader {
|
||||||
|
|
||||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final int nestedLimit, maxBufferSize;
|
private final int nestedLimit, maxBufferSize;
|
||||||
|
private final boolean canonical;
|
||||||
|
|
||||||
private boolean hasLookahead = false, eof = false;
|
private boolean hasLookahead = false, eof = false;
|
||||||
private byte next;
|
private byte next;
|
||||||
private byte[] buf = new byte[8];
|
private byte[] buf = new byte[8];
|
||||||
|
|
||||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
|
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize,
|
||||||
|
boolean canonical) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.nestedLimit = nestedLimit;
|
this.nestedLimit = nestedLimit;
|
||||||
this.maxBufferSize = maxBufferSize;
|
this.maxBufferSize = maxBufferSize;
|
||||||
|
this.canonical = canonical;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readLookahead() throws IOException {
|
private void readLookahead() throws IOException {
|
||||||
@@ -188,13 +191,22 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
|
|
||||||
private short readInt16() throws IOException {
|
private short readInt16() throws IOException {
|
||||||
readIntoBuffer(2);
|
readIntoBuffer(2);
|
||||||
return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
short value = (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||||
|
if (canonical && value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||||
|
// Value could have been encoded as an INT_8
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readInt32() throws IOException {
|
private int readInt32() throws IOException {
|
||||||
readIntoBuffer(4);
|
readIntoBuffer(4);
|
||||||
int value = 0;
|
int value = 0;
|
||||||
for (int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
for (int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
||||||
|
if (canonical && value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||||
|
// Value could have been encoded as an INT_16
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +214,11 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
readIntoBuffer(8);
|
readIntoBuffer(8);
|
||||||
long value = 0;
|
long value = 0;
|
||||||
for (int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
for (int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||||
|
if (canonical && value >= Integer.MIN_VALUE &&
|
||||||
|
value <= Integer.MAX_VALUE) {
|
||||||
|
// Value could have been encoded as an INT_32
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +232,31 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasInt() throws IOException {
|
||||||
|
if (!hasLookahead) readLookahead();
|
||||||
|
if (eof) return false;
|
||||||
|
return next == INT_8 || next == INT_16 || next == INT_32;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readInt() throws IOException {
|
||||||
|
if (!hasInt()) throw new FormatException();
|
||||||
|
hasLookahead = false;
|
||||||
|
if (next == INT_8) return readInt8();
|
||||||
|
if (next == INT_16) return readInt16();
|
||||||
|
return readInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skipInt() throws IOException {
|
||||||
|
if (!hasInt()) throw new FormatException();
|
||||||
|
if (next == INT_8) skip(1);
|
||||||
|
else if (next == INT_16) skip(2);
|
||||||
|
else skip(4);
|
||||||
|
hasLookahead = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDouble() throws IOException {
|
public boolean hasDouble() throws IOException {
|
||||||
if (!hasLookahead) readLookahead();
|
if (!hasLookahead) readLookahead();
|
||||||
@@ -323,22 +365,11 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
private BdfList readList(int level) throws IOException {
|
private BdfList readList(int level) throws IOException {
|
||||||
if (!hasList()) throw new FormatException();
|
if (!hasList()) throw new FormatException();
|
||||||
if (level > nestedLimit) throw new FormatException();
|
if (level > nestedLimit) throw new FormatException();
|
||||||
BdfList list = new BdfList();
|
|
||||||
readListStart();
|
|
||||||
while (!hasListEnd()) list.add(readObject(level + 1));
|
|
||||||
readListEnd();
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readListStart() throws IOException {
|
|
||||||
if (!hasList()) throw new FormatException();
|
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
}
|
BdfList list = new BdfList();
|
||||||
|
while (!hasEnd()) list.add(readObject(level + 1));
|
||||||
@Override
|
readEnd();
|
||||||
public boolean hasListEnd() throws IOException {
|
return list;
|
||||||
return hasEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasEnd() throws IOException {
|
private boolean hasEnd() throws IOException {
|
||||||
@@ -347,11 +378,6 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
return next == END;
|
return next == END;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readListEnd() throws IOException {
|
|
||||||
readEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readEnd() throws IOException {
|
private void readEnd() throws IOException {
|
||||||
if (!hasEnd()) throw new FormatException();
|
if (!hasEnd()) throw new FormatException();
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
@@ -361,7 +387,7 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
public void skipList() throws IOException {
|
public void skipList() throws IOException {
|
||||||
if (!hasList()) throw new FormatException();
|
if (!hasList()) throw new FormatException();
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
while (!hasListEnd()) skipObject();
|
while (!hasEnd()) skipObject();
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,35 +406,27 @@ class BdfReaderImpl implements BdfReader {
|
|||||||
private BdfDictionary readDictionary(int level) throws IOException {
|
private BdfDictionary readDictionary(int level) throws IOException {
|
||||||
if (!hasDictionary()) throw new FormatException();
|
if (!hasDictionary()) throw new FormatException();
|
||||||
if (level > nestedLimit) throw new FormatException();
|
if (level > nestedLimit) throw new FormatException();
|
||||||
BdfDictionary dictionary = new BdfDictionary();
|
|
||||||
readDictionaryStart();
|
|
||||||
while (!hasDictionaryEnd())
|
|
||||||
dictionary.put(readString(), readObject(level + 1));
|
|
||||||
readDictionaryEnd();
|
|
||||||
return dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readDictionaryStart() throws IOException {
|
|
||||||
if (!hasDictionary()) throw new FormatException();
|
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
}
|
BdfDictionary dictionary = new BdfDictionary();
|
||||||
|
String prevKey = null;
|
||||||
@Override
|
while (!hasEnd()) {
|
||||||
public boolean hasDictionaryEnd() throws IOException {
|
String key = readString();
|
||||||
return hasEnd();
|
if (canonical && prevKey != null && key.compareTo(prevKey) <= 0) {
|
||||||
}
|
// Keys not unique and sorted
|
||||||
|
throw new FormatException();
|
||||||
@Override
|
}
|
||||||
public void readDictionaryEnd() throws IOException {
|
dictionary.put(key, readObject(level + 1));
|
||||||
|
prevKey = key;
|
||||||
|
}
|
||||||
readEnd();
|
readEnd();
|
||||||
|
return dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void skipDictionary() throws IOException {
|
public void skipDictionary() throws IOException {
|
||||||
if (!hasDictionary()) throw new FormatException();
|
if (!hasDictionary()) throw new FormatException();
|
||||||
hasLookahead = false;
|
hasLookahead = false;
|
||||||
while (!hasDictionaryEnd()) {
|
while (!hasEnd()) {
|
||||||
skipString();
|
skipString();
|
||||||
skipObject();
|
skipObject();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package org.briarproject.bramble.data;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.api.data.BdfWriter;
|
import org.briarproject.bramble.api.data.BdfWriter;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -15,6 +17,7 @@ import java.util.Map.Entry;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
import static java.util.Collections.sort;
|
||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
import static org.briarproject.bramble.data.Types.DICTIONARY;
|
import static org.briarproject.bramble.data.Types.DICTIONARY;
|
||||||
import static org.briarproject.bramble.data.Types.END;
|
import static org.briarproject.bramble.data.Types.END;
|
||||||
@@ -33,10 +36,11 @@ import static org.briarproject.bramble.data.Types.STRING_16;
|
|||||||
import static org.briarproject.bramble.data.Types.STRING_32;
|
import static org.briarproject.bramble.data.Types.STRING_32;
|
||||||
import static org.briarproject.bramble.data.Types.STRING_8;
|
import static org.briarproject.bramble.data.Types.STRING_8;
|
||||||
import static org.briarproject.bramble.data.Types.TRUE;
|
import static org.briarproject.bramble.data.Types.TRUE;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class BdfWriterImpl implements BdfWriter {
|
final class BdfWriterImpl implements BdfWriter {
|
||||||
|
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
|
||||||
@@ -113,7 +117,7 @@ class BdfWriterImpl implements BdfWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeString(String s) throws IOException {
|
public void writeString(String s) throws IOException {
|
||||||
byte[] b = s.getBytes("UTF-8");
|
byte[] b = s.getBytes(UTF_8);
|
||||||
if (b.length <= Byte.MAX_VALUE) {
|
if (b.length <= Byte.MAX_VALUE) {
|
||||||
out.write(STRING_8);
|
out.write(STRING_8);
|
||||||
out.write((byte) b.length);
|
out.write((byte) b.length);
|
||||||
@@ -161,39 +165,33 @@ class BdfWriterImpl implements BdfWriter {
|
|||||||
else if (o instanceof String) writeString((String) o);
|
else if (o instanceof String) writeString((String) o);
|
||||||
else if (o instanceof byte[]) writeRaw((byte[]) o);
|
else if (o instanceof byte[]) writeRaw((byte[]) o);
|
||||||
else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
|
else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
|
||||||
else if (o instanceof List) writeList((List) o);
|
else if (o instanceof List) writeList((List<?>) o);
|
||||||
else if (o instanceof Map) writeDictionary((Map) o);
|
else if (o instanceof Map) writeDictionary((Map<?, ?>) o);
|
||||||
else throw new FormatException();
|
else throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeListStart() throws IOException {
|
|
||||||
out.write(LIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeListEnd() throws IOException {
|
|
||||||
out.write(END);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeDictionary(Map<?, ?> m) throws IOException {
|
public void writeDictionary(Map<?, ?> m) throws IOException {
|
||||||
out.write(DICTIONARY);
|
out.write(DICTIONARY);
|
||||||
for (Entry<?, ?> e : m.entrySet()) {
|
if (m instanceof BdfDictionary) {
|
||||||
if (!(e.getKey() instanceof String)) throw new FormatException();
|
// Entries are already sorted and keys are known to be strings
|
||||||
writeString((String) e.getKey());
|
for (Entry<String, Object> e : ((BdfDictionary) m).entrySet()) {
|
||||||
writeObject(e.getValue());
|
writeString(e.getKey());
|
||||||
|
writeObject(e.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check that keys are strings, write entries in canonical order
|
||||||
|
List<String> keys = new ArrayList<>(m.size());
|
||||||
|
for (Object k : m.keySet()) {
|
||||||
|
if (!(k instanceof String)) throw new FormatException();
|
||||||
|
keys.add((String) k);
|
||||||
|
}
|
||||||
|
sort(keys);
|
||||||
|
for (String key : keys) {
|
||||||
|
writeString(key);
|
||||||
|
writeObject(m.get(key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out.write(END);
|
out.write(END);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeDictionaryStart() throws IOException {
|
|
||||||
out.write(DICTIONARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeDictionaryEnd() throws IOException {
|
|
||||||
out.write(END);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.BdfWriter;
|
import org.briarproject.bramble.api.data.BdfWriter;
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
@@ -32,13 +33,14 @@ class PayloadEncoderImpl implements PayloadEncoder {
|
|||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||||
out.write(formatIdAndVersion);
|
out.write(formatIdAndVersion);
|
||||||
|
BdfList payload = new BdfList();
|
||||||
|
payload.add(p.getCommitment());
|
||||||
|
for (TransportDescriptor d : p.getTransportDescriptors()) {
|
||||||
|
payload.add(d.getDescriptor());
|
||||||
|
}
|
||||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||||
try {
|
try {
|
||||||
w.writeListStart(); // Payload start
|
w.writeList(payload);
|
||||||
w.writeRaw(p.getCommitment());
|
|
||||||
for (TransportDescriptor d : p.getTransportDescriptors())
|
|
||||||
w.writeList(d.getDescriptor());
|
|
||||||
w.writeListEnd(); // Payload end
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Shouldn't happen with ByteArrayOutputStream
|
// Shouldn't happen with ByteArrayOutputStream
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class PayloadParserImpl implements PayloadParser {
|
|||||||
List<TransportDescriptor> recognised = new ArrayList<>();
|
List<TransportDescriptor> recognised = new ArrayList<>();
|
||||||
for (int i = 1; i < payload.size(); i++) {
|
for (int i = 1; i < payload.size(); i++) {
|
||||||
BdfList descriptor = payload.getList(i);
|
BdfList descriptor = payload.getList(i);
|
||||||
long transportId = descriptor.getLong(0);
|
int transportId = descriptor.getInt(0);
|
||||||
if (transportId == TRANSPORT_ID_BLUETOOTH) {
|
if (transportId == TRANSPORT_ID_BLUETOOTH) {
|
||||||
TransportId id = BluetoothConstants.ID;
|
TransportId id = BluetoothConstants.ID;
|
||||||
recognised.add(new TransportDescriptor(id, descriptor));
|
recognised.add(new TransportDescriptor(id, descriptor));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
@@ -11,12 +12,15 @@ import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
|||||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.util.Base32;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
@@ -26,6 +30,7 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -98,6 +103,22 @@ class MailboxManagerImpl implements MailboxManager {
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertBase32Payload(String base32Payload)
|
||||||
|
throws FormatException {
|
||||||
|
Pattern regex = Pattern.compile("(briar-mailbox://)?([a-z2-7]{104})");
|
||||||
|
Matcher matcher = regex.matcher(base32Payload);
|
||||||
|
if (!matcher.find()) throw new FormatException();
|
||||||
|
String base32 = matcher.group(2);
|
||||||
|
byte[] payloadBytes;
|
||||||
|
try {
|
||||||
|
payloadBytes = Base32.decode(base32, false);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
return new String(payloadBytes, ISO_8859_1);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkConnection() {
|
public boolean checkConnection() {
|
||||||
List<MailboxVersion> versions = null;
|
List<MailboxVersion> versions = null;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
@@ -76,14 +75,11 @@ public class MailboxModule {
|
|||||||
ValidationManager validationManager,
|
ValidationManager validationManager,
|
||||||
ClientHelper clientHelper,
|
ClientHelper clientHelper,
|
||||||
MetadataEncoder metadataEncoder,
|
MetadataEncoder metadataEncoder,
|
||||||
Clock clock,
|
Clock clock) {
|
||||||
FeatureFlags featureFlags) {
|
|
||||||
MailboxUpdateValidator validator = new MailboxUpdateValidator(
|
MailboxUpdateValidator validator = new MailboxUpdateValidator(
|
||||||
clientHelper, metadataEncoder, clock);
|
clientHelper, metadataEncoder, clock);
|
||||||
if (featureFlags.shouldEnableMailbox()) {
|
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
|
||||||
validationManager.registerMessageValidator(CLIENT_ID,
|
validator);
|
||||||
MAJOR_VERSION, validator);
|
|
||||||
}
|
|
||||||
return validator;
|
return validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,31 +91,26 @@ public class MailboxModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
MailboxUpdateManager provideMailboxUpdateManager(
|
MailboxUpdateManager provideMailboxUpdateManager(
|
||||||
FeatureFlags featureFlags,
|
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
ValidationManager validationManager, ContactManager contactManager,
|
ValidationManager validationManager, ContactManager contactManager,
|
||||||
ClientVersioningManager clientVersioningManager,
|
ClientVersioningManager clientVersioningManager,
|
||||||
MailboxSettingsManager mailboxSettingsManager,
|
MailboxSettingsManager mailboxSettingsManager,
|
||||||
MailboxUpdateManagerImpl mailboxUpdateManager) {
|
MailboxUpdateManagerImpl mailboxUpdateManager) {
|
||||||
if (featureFlags.shouldEnableMailbox()) {
|
lifecycleManager.registerOpenDatabaseHook(mailboxUpdateManager);
|
||||||
lifecycleManager.registerOpenDatabaseHook(mailboxUpdateManager);
|
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
|
||||||
validationManager.registerIncomingMessageHook(CLIENT_ID,
|
mailboxUpdateManager);
|
||||||
MAJOR_VERSION, mailboxUpdateManager);
|
contactManager.registerContactHook(mailboxUpdateManager);
|
||||||
contactManager.registerContactHook(mailboxUpdateManager);
|
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
||||||
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
MINOR_VERSION, mailboxUpdateManager);
|
||||||
MINOR_VERSION, mailboxUpdateManager);
|
mailboxSettingsManager.registerMailboxHook(mailboxUpdateManager);
|
||||||
mailboxSettingsManager.registerMailboxHook(mailboxUpdateManager);
|
|
||||||
}
|
|
||||||
return mailboxUpdateManager;
|
return mailboxUpdateManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
MailboxFileManager provideMailboxFileManager(FeatureFlags featureFlags,
|
MailboxFileManager provideMailboxFileManager(EventBus eventBus,
|
||||||
EventBus eventBus, MailboxFileManagerImpl mailboxFileManager) {
|
MailboxFileManagerImpl mailboxFileManager) {
|
||||||
if (featureFlags.shouldEnableMailbox()) {
|
eventBus.addListener(mailboxFileManager);
|
||||||
eventBus.addListener(mailboxFileManager);
|
|
||||||
}
|
|
||||||
return mailboxFileManager;
|
return mailboxFileManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,17 +151,14 @@ public class MailboxModule {
|
|||||||
MailboxUpdateManager mailboxUpdateManager,
|
MailboxUpdateManager mailboxUpdateManager,
|
||||||
MailboxClientFactory mailboxClientFactory,
|
MailboxClientFactory mailboxClientFactory,
|
||||||
TorReachabilityMonitor reachabilityMonitor,
|
TorReachabilityMonitor reachabilityMonitor,
|
||||||
FeatureFlags featureFlags,
|
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
EventBus eventBus) {
|
EventBus eventBus) {
|
||||||
MailboxClientManager manager = new MailboxClientManager(eventExecutor,
|
MailboxClientManager manager = new MailboxClientManager(eventExecutor,
|
||||||
dbExecutor, db, contactManager, pluginManager,
|
dbExecutor, db, contactManager, pluginManager,
|
||||||
mailboxSettingsManager, mailboxUpdateManager,
|
mailboxSettingsManager, mailboxUpdateManager,
|
||||||
mailboxClientFactory, reachabilityMonitor);
|
mailboxClientFactory, reachabilityMonitor);
|
||||||
if (featureFlags.shouldEnableMailbox()) {
|
lifecycleManager.registerService(manager);
|
||||||
lifecycleManager.registerService(manager);
|
eventBus.addListener(manager);
|
||||||
eventBus.addListener(manager);
|
|
||||||
}
|
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -288,8 +288,10 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
private class Callback implements PluginCallback {
|
private class Callback implements PluginCallback {
|
||||||
|
|
||||||
private final TransportId id;
|
private final TransportId id;
|
||||||
private final AtomicReference<State> state =
|
private final Object stateLock = new Object();
|
||||||
new AtomicReference<>(STARTING_STOPPING);
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private State state = STARTING_STOPPING;
|
||||||
|
|
||||||
private Callback(TransportId id) {
|
private Callback(TransportId id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -343,22 +345,26 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginStateChanged(State newState) {
|
public void pluginStateChanged(State newState) {
|
||||||
State oldState = state.getAndSet(newState);
|
synchronized (stateLock) {
|
||||||
if (newState != oldState) {
|
if (newState != state) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
State oldState = state;
|
||||||
LOG.info(id + " changed from state " + oldState
|
state = newState;
|
||||||
+ " to " + newState);
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info(id + " changed from state " + oldState
|
||||||
|
+ " to " + newState);
|
||||||
|
}
|
||||||
|
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||||
|
if (newState == ACTIVE) {
|
||||||
|
eventBus.broadcast(new TransportActiveEvent(id));
|
||||||
|
} else if (oldState == ACTIVE) {
|
||||||
|
eventBus.broadcast(new TransportInactiveEvent(id));
|
||||||
|
}
|
||||||
|
} else if (newState == DISABLED) {
|
||||||
|
// Broadcast an event even though the state hasn't changed,
|
||||||
|
// as the reasons for the plugin being disabled may have
|
||||||
|
// changed
|
||||||
|
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||||
}
|
}
|
||||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
|
||||||
if (newState == ACTIVE) {
|
|
||||||
eventBus.broadcast(new TransportActiveEvent(id));
|
|
||||||
} else if (oldState == ACTIVE) {
|
|
||||||
eventBus.broadcast(new TransportInactiveEvent(id));
|
|
||||||
}
|
|
||||||
} else if (newState == DISABLED) {
|
|
||||||
// Broadcast an event even though the state hasn't changed, as
|
|
||||||
// the reasons for the plugin being disabled may have changed
|
|
||||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
private InetSocketAddress parseSocketAddress(BdfList descriptor)
|
private InetSocketAddress parseSocketAddress(BdfList descriptor)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
byte[] address = descriptor.getRaw(1);
|
byte[] address = descriptor.getRaw(1);
|
||||||
int port = descriptor.getLong(2).intValue();
|
int port = descriptor.getInt(2);
|
||||||
if (port < 1 || port > MAX_16_BIT_UNSIGNED) throw new FormatException();
|
if (port < 1 || port > MAX_16_BIT_UNSIGNED) throw new FormatException();
|
||||||
try {
|
try {
|
||||||
InetAddress addr = InetAddress.getByAddress(address);
|
InetAddress addr = InetAddress.getByAddress(address);
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface CircumventionProvider {
|
|
||||||
|
|
||||||
enum BridgeType {
|
|
||||||
DEFAULT_OBFS4,
|
|
||||||
NON_DEFAULT_OBFS4,
|
|
||||||
VANILLA,
|
|
||||||
MEEK,
|
|
||||||
SNOWFLAKE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countries where Tor is blocked, i.e. vanilla Tor connection won't work.
|
|
||||||
* <p>
|
|
||||||
* See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
|
||||||
* and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki
|
|
||||||
*/
|
|
||||||
String[] BLOCKED = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countries where bridge connections are likely to work.
|
|
||||||
* Should be a subset of {@link #BLOCKED} and the union of
|
|
||||||
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
|
||||||
* {@link #DPI_BRIDGES}.
|
|
||||||
*/
|
|
||||||
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countries where default obfs4 or vanilla bridges are likely to work.
|
|
||||||
* Should be a subset of {@link #BRIDGES}.
|
|
||||||
*/
|
|
||||||
String[] DEFAULT_BRIDGES = {"EG", "VE"};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countries where non-default obfs4 or vanilla bridges are likely to work.
|
|
||||||
* Should be a subset of {@link #BRIDGES}.
|
|
||||||
*/
|
|
||||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU"};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countries where vanilla bridges are blocked via DPI but non-default
|
|
||||||
* obfs4 bridges, meek and snowflake may work. Should be a subset of
|
|
||||||
* {@link #BRIDGES}.
|
|
||||||
*/
|
|
||||||
String[] DPI_BRIDGES = {"CN", "IR", "TM"};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if vanilla Tor connections are blocked in the given country.
|
|
||||||
*/
|
|
||||||
boolean isTorProbablyBlocked(String countryCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if bridge connections of some type work in the given
|
|
||||||
* country.
|
|
||||||
*/
|
|
||||||
boolean doBridgesWork(String countryCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the types of bridge connection that are suitable for the given
|
|
||||||
* country, or {@link #DEFAULT_BRIDGES} if no bridge type is known
|
|
||||||
* to work.
|
|
||||||
*/
|
|
||||||
List<BridgeType> getSuitableBridgeTypes(String countryCode);
|
|
||||||
|
|
||||||
@IoExecutor
|
|
||||||
List<String> getBridges(BridgeType type, String countryCode,
|
|
||||||
boolean letsEncrypt);
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
|
||||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class CircumventionProviderImpl implements CircumventionProvider {
|
|
||||||
|
|
||||||
private final static String BRIDGE_FILE_NAME = "bridges";
|
|
||||||
private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params";
|
|
||||||
private final static String DEFAULT_COUNTRY_CODE = "ZZ";
|
|
||||||
|
|
||||||
private static final Set<String> BLOCKED_IN_COUNTRIES =
|
|
||||||
new HashSet<>(asList(BLOCKED));
|
|
||||||
private static final Set<String> BRIDGE_COUNTRIES =
|
|
||||||
new HashSet<>(asList(BRIDGES));
|
|
||||||
private static final Set<String> DEFAULT_OBFS4_BRIDGE_COUNTRIES =
|
|
||||||
new HashSet<>(asList(DEFAULT_BRIDGES));
|
|
||||||
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
|
||||||
private static final Set<String> DPI_COUNTRIES =
|
|
||||||
new HashSet<>(asList(DPI_BRIDGES));
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CircumventionProviderImpl() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTorProbablyBlocked(String countryCode) {
|
|
||||||
return BLOCKED_IN_COUNTRIES.contains(countryCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doBridgesWork(String countryCode) {
|
|
||||||
return BRIDGE_COUNTRIES.contains(countryCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<BridgeType> getSuitableBridgeTypes(String countryCode) {
|
|
||||||
if (DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) {
|
|
||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
|
||||||
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
|
||||||
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
|
||||||
} else if (DPI_COUNTRIES.contains(countryCode)) {
|
|
||||||
return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE);
|
|
||||||
} else {
|
|
||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@IoExecutor
|
|
||||||
public List<String> getBridges(BridgeType type, String countryCode,
|
|
||||||
boolean letsEncrypt) {
|
|
||||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
|
||||||
.getResourceAsStream(BRIDGE_FILE_NAME));
|
|
||||||
Scanner scanner = new Scanner(is);
|
|
||||||
|
|
||||||
List<String> bridges = new ArrayList<>();
|
|
||||||
while (scanner.hasNextLine()) {
|
|
||||||
String line = scanner.nextLine();
|
|
||||||
if ((type == DEFAULT_OBFS4 && line.startsWith("d ")) ||
|
|
||||||
(type == NON_DEFAULT_OBFS4 && line.startsWith("n ")) ||
|
|
||||||
(type == VANILLA && line.startsWith("v ")) ||
|
|
||||||
(type == MEEK && line.startsWith("m "))) {
|
|
||||||
bridges.add(line.substring(2));
|
|
||||||
} else if (type == SNOWFLAKE && line.startsWith("s ")) {
|
|
||||||
String params = getSnowflakeParams(countryCode, letsEncrypt);
|
|
||||||
bridges.add(line.substring(2) + " " + params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scanner.close();
|
|
||||||
return bridges;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
String getSnowflakeParams(String countryCode, boolean letsEncrypt) {
|
|
||||||
Map<String, String> params = loadSnowflakeParams();
|
|
||||||
if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE;
|
|
||||||
// If we have parameters for this country code, return them
|
|
||||||
String value = params.get(makeKey(countryCode, letsEncrypt));
|
|
||||||
if (value != null) return value;
|
|
||||||
// Return the default parameters
|
|
||||||
value = params.get(makeKey(DEFAULT_COUNTRY_CODE, letsEncrypt));
|
|
||||||
return requireNonNull(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> loadSnowflakeParams() {
|
|
||||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
|
||||||
.getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME));
|
|
||||||
Scanner scanner = new Scanner(is);
|
|
||||||
Map<String, String> params = new TreeMap<>();
|
|
||||||
while (scanner.hasNextLine()) {
|
|
||||||
String line = scanner.nextLine();
|
|
||||||
if (line.length() < 5) continue;
|
|
||||||
String key = line.substring(0, 4); // Country code, space, digit
|
|
||||||
String value = line.substring(5);
|
|
||||||
params.put(key, value);
|
|
||||||
}
|
|
||||||
scanner.close();
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeKey(String countryCode, boolean letsEncrypt) {
|
|
||||||
return countryCode + " " + (letsEncrypt ? "1" : "0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
import net.freehaven.tor.control.EventHandler;
|
|
||||||
import net.freehaven.tor.control.TorControlConnection;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.PoliteExecutor;
|
import org.briarproject.bramble.PoliteExecutor;
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
@@ -27,35 +24,27 @@ import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
|||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
|
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider.BridgeType;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper.Observer;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper.TorState;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
@@ -64,13 +53,9 @@ import javax.net.SocketFactory;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
|
||||||
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
||||||
@@ -92,35 +77,19 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
|
|||||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
|
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
|
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
|
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
|
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
|
||||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK;
|
||||||
|
import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@InterfaceNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
class TorPlugin implements DuplexPlugin, EventListener {
|
||||||
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|
||||||
|
|
||||||
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
private static final String[] EVENTS = {
|
|
||||||
"CIRC",
|
|
||||||
"ORCONN",
|
|
||||||
"STATUS_GENERAL",
|
|
||||||
"STATUS_CLIENT",
|
|
||||||
"HS_DESC",
|
|
||||||
"NOTICE",
|
|
||||||
"WARN",
|
|
||||||
"ERR"
|
|
||||||
};
|
|
||||||
private static final String OWNER = "__OwningControllerProcess";
|
|
||||||
private static final int COOKIE_TIMEOUT_MS = 3000;
|
|
||||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
|
||||||
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
protected final Executor ioExecutor;
|
protected final Executor ioExecutor;
|
||||||
@@ -129,91 +98,79 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private final NetworkManager networkManager;
|
private final NetworkManager networkManager;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
private final SocketFactory torSocketFactory;
|
private final SocketFactory torSocketFactory;
|
||||||
private final Clock clock;
|
private final CircumventionProvider circumventionProvider;
|
||||||
private final BatteryManager batteryManager;
|
private final BatteryManager batteryManager;
|
||||||
private final Backoff backoff;
|
private final Backoff backoff;
|
||||||
private final TorRendezvousCrypto torRendezvousCrypto;
|
private final TorRendezvousCrypto torRendezvousCrypto;
|
||||||
|
private final TorWrapper tor;
|
||||||
private final PluginCallback callback;
|
private final PluginCallback callback;
|
||||||
private final String architecture;
|
|
||||||
private final CircumventionProvider circumventionProvider;
|
|
||||||
private final ResourceProvider resourceProvider;
|
|
||||||
private final long maxLatency;
|
private final long maxLatency;
|
||||||
private final int maxIdleTime;
|
private final int maxIdleTime;
|
||||||
|
private final boolean canVerifyLetsEncryptCerts;
|
||||||
private final int socketTimeout;
|
private final int socketTimeout;
|
||||||
private final File torDirectory;
|
|
||||||
private final File configFile;
|
|
||||||
private final int torSocksPort;
|
|
||||||
private final int torControlPort;
|
|
||||||
private final File doneFile, cookieFile;
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
protected final PluginState state = new PluginState();
|
protected final PluginState state = new PluginState();
|
||||||
|
|
||||||
private volatile Socket controlSocket = null;
|
|
||||||
private volatile TorControlConnection controlConnection = null;
|
|
||||||
private volatile Settings settings = null;
|
private volatile Settings settings = null;
|
||||||
|
|
||||||
protected abstract int getProcessId();
|
|
||||||
|
|
||||||
protected abstract long getLastUpdateTime();
|
|
||||||
|
|
||||||
TorPlugin(Executor ioExecutor,
|
TorPlugin(Executor ioExecutor,
|
||||||
Executor wakefulIoExecutor,
|
Executor wakefulIoExecutor,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
Clock clock,
|
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
Backoff backoff,
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
|
TorWrapper tor,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
|
||||||
long maxLatency,
|
long maxLatency,
|
||||||
int maxIdleTime,
|
int maxIdleTime,
|
||||||
File torDirectory,
|
boolean canVerifyLetsEncryptCerts) {
|
||||||
int torSocksPort,
|
|
||||||
int torControlPort) {
|
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||||
this.networkManager = networkManager;
|
this.networkManager = networkManager;
|
||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.torSocketFactory = torSocketFactory;
|
this.torSocketFactory = torSocketFactory;
|
||||||
this.clock = clock;
|
|
||||||
this.resourceProvider = resourceProvider;
|
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
this.backoff = backoff;
|
this.backoff = backoff;
|
||||||
this.torRendezvousCrypto = torRendezvousCrypto;
|
this.torRendezvousCrypto = torRendezvousCrypto;
|
||||||
|
this.tor = tor;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.architecture = architecture;
|
|
||||||
this.maxLatency = maxLatency;
|
this.maxLatency = maxLatency;
|
||||||
this.maxIdleTime = maxIdleTime;
|
this.maxIdleTime = maxIdleTime;
|
||||||
if (maxIdleTime > Integer.MAX_VALUE / 2)
|
this.canVerifyLetsEncryptCerts = canVerifyLetsEncryptCerts;
|
||||||
|
if (maxIdleTime > Integer.MAX_VALUE / 2) {
|
||||||
socketTimeout = Integer.MAX_VALUE;
|
socketTimeout = Integer.MAX_VALUE;
|
||||||
else socketTimeout = maxIdleTime * 2;
|
} else {
|
||||||
this.torDirectory = torDirectory;
|
socketTimeout = maxIdleTime * 2;
|
||||||
this.torSocksPort = torSocksPort;
|
}
|
||||||
this.torControlPort = torControlPort;
|
|
||||||
configFile = new File(torDirectory, "torrc");
|
|
||||||
doneFile = new File(torDirectory, "done");
|
|
||||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
|
||||||
// Don't execute more than one connection status check at a time
|
// Don't execute more than one connection status check at a time
|
||||||
connectionStatusExecutor =
|
connectionStatusExecutor =
|
||||||
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
||||||
}
|
tor.setObserver(new Observer() {
|
||||||
|
|
||||||
protected File getTorExecutableFile() {
|
@Override
|
||||||
return new File(torDirectory, "tor");
|
public void onState(TorState torState) {
|
||||||
}
|
State s = state.getState(torState);
|
||||||
|
if (s == ACTIVE) backoff.reset();
|
||||||
|
callback.pluginStateChanged(s);
|
||||||
|
}
|
||||||
|
|
||||||
protected File getObfs4ExecutableFile() {
|
@Override
|
||||||
return new File(torDirectory, "obfs4proxy");
|
public void onBootstrapPercentage(int percentage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File getSnowflakeExecutableFile() {
|
@Override
|
||||||
return new File(torDirectory, "snowflake");
|
public void onHsDescriptorUpload(String onion) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClockSkewDetected(long skewSeconds) {
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -234,89 +191,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void start() throws PluginException {
|
public void start() throws PluginException {
|
||||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||||
if (!torDirectory.exists()) {
|
|
||||||
if (!torDirectory.mkdirs()) {
|
|
||||||
LOG.warning("Could not create Tor directory.");
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Load the settings
|
// Load the settings
|
||||||
settings = callback.getSettings();
|
settings = callback.getSettings();
|
||||||
|
// Start Tor
|
||||||
try {
|
try {
|
||||||
// Install or update the assets if necessary
|
tor.start();
|
||||||
if (!assetsAreUpToDate()) installAssets();
|
|
||||||
// Start from the default config every time
|
|
||||||
extract(getConfigInputStream(), configFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new PluginException(e);
|
|
||||||
}
|
|
||||||
if (cookieFile.exists() && !cookieFile.delete())
|
|
||||||
LOG.warning("Old auth cookie not deleted");
|
|
||||||
// Start a new Tor process
|
|
||||||
LOG.info("Starting Tor");
|
|
||||||
File torFile = getTorExecutableFile();
|
|
||||||
String torPath = torFile.getAbsolutePath();
|
|
||||||
String configPath = configFile.getAbsolutePath();
|
|
||||||
String pid = String.valueOf(getProcessId());
|
|
||||||
Process torProcess;
|
|
||||||
ProcessBuilder pb =
|
|
||||||
new ProcessBuilder(torPath, "-f", configPath, OWNER, pid);
|
|
||||||
Map<String, String> env = pb.environment();
|
|
||||||
env.put("HOME", torDirectory.getAbsolutePath());
|
|
||||||
pb.directory(torDirectory);
|
|
||||||
pb.redirectErrorStream(true);
|
|
||||||
try {
|
|
||||||
torProcess = pb.start();
|
|
||||||
} catch (SecurityException | IOException e) {
|
|
||||||
throw new PluginException(e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Wait for the Tor process to start
|
|
||||||
waitForTorToStart(torProcess);
|
|
||||||
// Wait for the auth cookie file to be created/updated
|
|
||||||
long start = clock.currentTimeMillis();
|
|
||||||
while (cookieFile.length() < 32) {
|
|
||||||
if (clock.currentTimeMillis() - start > COOKIE_TIMEOUT_MS) {
|
|
||||||
LOG.warning("Auth cookie not created");
|
|
||||||
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
//noinspection BusyWait
|
|
||||||
Thread.sleep(COOKIE_POLLING_INTERVAL_MS);
|
|
||||||
}
|
|
||||||
LOG.info("Auth cookie created");
|
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.warning("Interrupted while starting Tor");
|
LOG.warning("Interrupted while starting Tor");
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
throw new PluginException();
|
throw new PluginException();
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Open a control connection and authenticate using the cookie file
|
|
||||||
controlSocket = new Socket("127.0.0.1", torControlPort);
|
|
||||||
controlConnection = new TorControlConnection(controlSocket);
|
|
||||||
controlConnection.authenticate(read(cookieFile));
|
|
||||||
// Tell Tor to exit when the control connection is closed
|
|
||||||
controlConnection.takeOwnership();
|
|
||||||
controlConnection.resetConf(singletonList(OWNER));
|
|
||||||
// Register to receive events from the Tor process
|
|
||||||
controlConnection.setEventHandler(this);
|
|
||||||
controlConnection.setEvents(asList(EVENTS));
|
|
||||||
// Check whether Tor has already bootstrapped
|
|
||||||
String info = controlConnection.getInfo("status/bootstrap-phase");
|
|
||||||
if (info != null && info.contains("PROGRESS=100")) {
|
|
||||||
LOG.info("Tor has already bootstrapped");
|
|
||||||
state.setBootstrapped();
|
|
||||||
}
|
|
||||||
// Check whether Tor has already built a circuit
|
|
||||||
info = controlConnection.getInfo("status/circuit-established");
|
|
||||||
if ("1".equals(info)) {
|
|
||||||
LOG.info("Tor has already built a circuit");
|
|
||||||
state.setCircuitBuilt(true);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
state.setStarted();
|
|
||||||
// Check whether we're online
|
// Check whether we're online
|
||||||
updateConnectionStatus(networkManager.getNetworkStatus(),
|
updateConnectionStatus(networkManager.getNetworkStatus(),
|
||||||
batteryManager.isCharging());
|
batteryManager.isCharging());
|
||||||
@@ -324,140 +210,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
bind();
|
bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean assetsAreUpToDate() {
|
|
||||||
return doneFile.lastModified() > getLastUpdateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void installAssets() throws IOException {
|
|
||||||
// The done file may already exist from a previous installation
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
doneFile.delete();
|
|
||||||
installTorExecutable();
|
|
||||||
installObfs4Executable();
|
|
||||||
installSnowflakeExecutable();
|
|
||||||
if (!doneFile.createNewFile())
|
|
||||||
LOG.warning("Failed to create done file");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void extract(InputStream in, File dest) throws IOException {
|
|
||||||
OutputStream out = new FileOutputStream(dest);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void installTorExecutable() throws IOException {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Installing Tor binary for " + architecture);
|
|
||||||
File torFile = getTorExecutableFile();
|
|
||||||
extract(getTorInputStream(), torFile);
|
|
||||||
if (!torFile.setExecutable(true, true)) throw new IOException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void installObfs4Executable() throws IOException {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Installing obfs4proxy binary for " + architecture);
|
|
||||||
File obfs4File = getObfs4ExecutableFile();
|
|
||||||
extract(getObfs4InputStream(), obfs4File);
|
|
||||||
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void installSnowflakeExecutable() throws IOException {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Installing snowflake binary for " + architecture);
|
|
||||||
File snowflakeFile = getSnowflakeExecutableFile();
|
|
||||||
extract(getSnowflakeInputStream(), snowflakeFile);
|
|
||||||
if (!snowflakeFile.setExecutable(true, true)) throw new IOException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getTorInputStream() throws IOException {
|
|
||||||
return getZipInputStream("tor");
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getObfs4InputStream() throws IOException {
|
|
||||||
return getZipInputStream("obfs4proxy");
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getSnowflakeInputStream() throws IOException {
|
|
||||||
return getZipInputStream("snowflake");
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getZipInputStream(String basename) throws IOException {
|
|
||||||
InputStream in = resourceProvider
|
|
||||||
.getResourceInputStream(basename + "_" + architecture, ".zip");
|
|
||||||
ZipInputStream zin = new ZipInputStream(in);
|
|
||||||
if (zin.getNextEntry() == null) throw new IOException();
|
|
||||||
return zin;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void append(StringBuilder strb, String name, Object value) {
|
|
||||||
strb.append(name);
|
|
||||||
strb.append(" ");
|
|
||||||
strb.append(value);
|
|
||||||
strb.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getConfigInputStream() {
|
|
||||||
File dataDirectory = new File(torDirectory, ".tor");
|
|
||||||
StringBuilder strb = new StringBuilder();
|
|
||||||
append(strb, "ControlPort", torControlPort);
|
|
||||||
append(strb, "CookieAuthentication", 1);
|
|
||||||
append(strb, "DataDirectory", dataDirectory.getAbsolutePath());
|
|
||||||
append(strb, "DisableNetwork", 1);
|
|
||||||
append(strb, "RunAsDaemon", 1);
|
|
||||||
append(strb, "SafeSocks", 1);
|
|
||||||
append(strb, "SocksPort", torSocksPort);
|
|
||||||
strb.append("GeoIPFile\n");
|
|
||||||
strb.append("GeoIPv6File\n");
|
|
||||||
append(strb, "ConnectionPadding", 0);
|
|
||||||
String obfs4Path = getObfs4ExecutableFile().getAbsolutePath();
|
|
||||||
append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path);
|
|
||||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
|
||||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
|
||||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
|
||||||
return new ByteArrayInputStream(strb.toString().getBytes(UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void listFiles(File f) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
File[] children = f.listFiles();
|
|
||||||
if (children != null) for (File child : children) listFiles(child);
|
|
||||||
} else {
|
|
||||||
LOG.info(f.getAbsolutePath() + " " + f.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] read(File f) throws IOException {
|
|
||||||
byte[] b = new byte[(int) f.length()];
|
|
||||||
FileInputStream in = new FileInputStream(f);
|
|
||||||
try {
|
|
||||||
int offset = 0;
|
|
||||||
while (offset < b.length) {
|
|
||||||
int read = in.read(b, offset, b.length - offset);
|
|
||||||
if (read == -1) throw new EOFException();
|
|
||||||
offset += read;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
} finally {
|
|
||||||
tryToClose(in, LOG, WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void waitForTorToStart(Process torProcess)
|
|
||||||
throws InterruptedException, PluginException {
|
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
|
||||||
// Log the first line of stdout (contains Tor and library versions)
|
|
||||||
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
|
||||||
// Read the process's stdout (and redirected stderr) until it detaches
|
|
||||||
while (stdout.hasNextLine()) stdout.nextLine();
|
|
||||||
stdout.close();
|
|
||||||
// Wait for the process to detach or exit
|
|
||||||
int exit = torProcess.waitFor();
|
|
||||||
if (exit != 0) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.warning("Tor exited with value " + exit);
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
@@ -481,9 +233,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Store the port number
|
// Store the port number
|
||||||
String localPort = String.valueOf(ss.getLocalPort());
|
int localPort = ss.getLocalPort();
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put(PREF_TOR_PORT, localPort);
|
s.put(PREF_TOR_PORT, String.valueOf(localPort));
|
||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(() -> publishHiddenService(localPort));
|
ioExecutor.execute(() -> publishHiddenService(localPort));
|
||||||
@@ -493,48 +245,28 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishHiddenService(String port) {
|
private void publishHiddenService(int localPort) {
|
||||||
if (!state.isTorRunning()) return;
|
if (!tor.isTorRunning()) return;
|
||||||
String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
|
String privKey = settings.get(HS_PRIVATE_KEY_V3);
|
||||||
publishV3HiddenService(port, privKey3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publishV3HiddenService(String port, @Nullable String privKey) {
|
|
||||||
LOG.info("Creating v3 hidden service");
|
LOG.info("Creating v3 hidden service");
|
||||||
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
|
HiddenServiceProperties hsProps;
|
||||||
Map<String, String> response;
|
|
||||||
try {
|
try {
|
||||||
// Use the control connection to set up the hidden service
|
hsProps = tor.publishHiddenService(localPort, 80, privKey);
|
||||||
if (privKey == null) {
|
|
||||||
response = controlConnection.addOnion("NEW:ED25519-V3",
|
|
||||||
portLines, null);
|
|
||||||
} else {
|
|
||||||
response = controlConnection.addOnion(privKey, portLines);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!response.containsKey(HS_ADDRESS)) {
|
|
||||||
LOG.warning("Tor did not return a hidden service address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (privKey == null && !response.containsKey(HS_PRIVKEY)) {
|
|
||||||
LOG.warning("Tor did not return a private key");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String onion3 = response.get(HS_ADDRESS);
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
LOG.info("V3 hidden service " + scrubOnion(hsProps.onion));
|
||||||
}
|
}
|
||||||
if (privKey == null) {
|
if (privKey == null) {
|
||||||
// Publish the hidden service's onion hostname in transport props
|
// Publish the hidden service's onion hostname in transport props
|
||||||
TransportProperties p = new TransportProperties();
|
TransportProperties p = new TransportProperties();
|
||||||
p.put(PROP_ONION_V3, onion3);
|
p.put(PROP_ONION_V3, hsProps.onion);
|
||||||
callback.mergeLocalProperties(p);
|
callback.mergeLocalProperties(p);
|
||||||
// Save the hidden service's private key for next time
|
// Save the hidden service's private key for next time
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
|
s.put(HS_PRIVATE_KEY_V3, hsProps.privKey);
|
||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,50 +289,28 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void enableNetwork(boolean enable) throws IOException {
|
|
||||||
if (!state.enableNetwork(enable)) return; // Unchanged
|
|
||||||
controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableBridges(List<BridgeType> bridgeTypes, String countryCode)
|
private void enableBridges(List<BridgeType> bridgeTypes, String countryCode)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged
|
|
||||||
if (bridgeTypes.isEmpty()) {
|
if (bridgeTypes.isEmpty()) {
|
||||||
controlConnection.setConf("UseBridges", "0");
|
tor.disableBridges();
|
||||||
controlConnection.resetConf(singletonList("Bridge"));
|
|
||||||
} else {
|
} else {
|
||||||
Collection<String> conf = new ArrayList<>();
|
List<String> bridges = new ArrayList<>();
|
||||||
conf.add("UseBridges 1");
|
|
||||||
boolean letsEncrypt = canVerifyLetsEncryptCerts();
|
|
||||||
for (BridgeType bridgeType : bridgeTypes) {
|
for (BridgeType bridgeType : bridgeTypes) {
|
||||||
conf.addAll(circumventionProvider
|
bridges.addAll(circumventionProvider.getBridges(bridgeType,
|
||||||
.getBridges(bridgeType, countryCode, letsEncrypt));
|
countryCode, canVerifyLetsEncryptCerts));
|
||||||
}
|
}
|
||||||
controlConnection.setConf(conf);
|
tor.enableBridges(bridges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this device can verify Let's Encrypt certificates signed
|
|
||||||
* with the IdentTrust DST Root X3 certificate, which expired at the end of
|
|
||||||
* September 2021.
|
|
||||||
*/
|
|
||||||
protected boolean canVerifyLetsEncryptCerts() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
ServerSocket ss = state.setStopped();
|
ServerSocket ss = state.setStopped();
|
||||||
tryToClose(ss, LOG, WARNING);
|
tryToClose(ss, LOG, WARNING);
|
||||||
if (controlSocket != null && controlConnection != null) {
|
try {
|
||||||
try {
|
tor.stop();
|
||||||
LOG.info("Stopping Tor");
|
} catch (IOException e) {
|
||||||
controlConnection.shutdownTor("TERM");
|
logException(LOG, WARNING, e);
|
||||||
controlSocket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,6 +421,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
TransportProperties remoteProperties = new TransportProperties();
|
TransportProperties remoteProperties = new TransportProperties();
|
||||||
remoteProperties.put(PROP_ONION_V3, remoteOnion);
|
remoteProperties.put(PROP_ONION_V3, remoteOnion);
|
||||||
try {
|
try {
|
||||||
|
@SuppressWarnings("resource")
|
||||||
ServerSocket ss = new ServerSocket();
|
ServerSocket ss = new ServerSocket();
|
||||||
ss.bind(new InetSocketAddress("127.0.0.1", 0));
|
ss.bind(new InetSocketAddress("127.0.0.1", 0));
|
||||||
int port = ss.getLocalPort();
|
int port = ss.getLocalPort();
|
||||||
@@ -727,9 +438,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.info("Rendezvous server socket closed");
|
LOG.info("Rendezvous server socket closed");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Map<Integer, String> portLines =
|
tor.publishHiddenService(port, 80, blob);
|
||||||
singletonMap(80, "127.0.0.1:" + port);
|
|
||||||
controlConnection.addOnion(blob, portLines);
|
|
||||||
return new RendezvousEndpoint() {
|
return new RendezvousEndpoint() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -739,8 +448,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
controlConnection.delOnion(localOnion);
|
try {
|
||||||
tryToClose(ss, LOG, WARNING);
|
tor.removeHiddenService(localOnion);
|
||||||
|
} finally {
|
||||||
|
tryToClose(ss, LOG, WARNING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -749,121 +461,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void circuitStatus(String status, String id, String path) {
|
|
||||||
// In case of races between receiving CIRCUIT_ESTABLISHED and setting
|
|
||||||
// DisableNetwork, set our circuitBuilt flag if not already set
|
|
||||||
if (status.equals("BUILT") && state.setCircuitBuilt(true)) {
|
|
||||||
LOG.info("Circuit built");
|
|
||||||
backoff.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void streamStatus(String status, String id, String target) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void orConnStatus(String status, String orName) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
|
|
||||||
|
|
||||||
if (status.equals("CONNECTED")) state.onOrConnectionConnected();
|
|
||||||
else if (status.equals("CLOSED")) state.onOrConnectionClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bandwidthUsed(long read, long written) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void newDescriptors(List<String> orList) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void message(String severity, String msg) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unrecognized(String type, String msg) {
|
|
||||||
if (type.equals("STATUS_CLIENT")) {
|
|
||||||
handleClientStatus(removeSeverity(msg));
|
|
||||||
} else if (type.equals("STATUS_GENERAL")) {
|
|
||||||
handleGeneralStatus(removeSeverity(msg));
|
|
||||||
} else if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
|
|
||||||
String[] parts = msg.split(" ");
|
|
||||||
if (parts.length < 2) {
|
|
||||||
LOG.warning("Failed to parse HS_DESC UPLOADED event");
|
|
||||||
} else if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("V3 descriptor uploaded for " + scrubOnion(parts[1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void controlConnectionClosed() {
|
|
||||||
if (state.isTorRunning()) {
|
|
||||||
// TODO: Restart the Tor process
|
|
||||||
LOG.warning("Control connection closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String removeSeverity(String msg) {
|
|
||||||
return msg.replaceFirst("[^ ]+ ", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleClientStatus(String msg) {
|
|
||||||
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
|
||||||
LOG.info("Bootstrapped");
|
|
||||||
state.setBootstrapped();
|
|
||||||
backoff.reset();
|
|
||||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
|
||||||
if (state.setCircuitBuilt(true)) {
|
|
||||||
LOG.info("Circuit built");
|
|
||||||
backoff.reset();
|
|
||||||
}
|
|
||||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
|
||||||
if (state.setCircuitBuilt(false)) {
|
|
||||||
LOG.info("Circuit not built");
|
|
||||||
// TODO: Disable and re-enable network to prompt Tor to rebuild
|
|
||||||
// its guard/bridge connections? This will also close any
|
|
||||||
// established circuits, which might still be functioning
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleGeneralStatus(String msg) {
|
|
||||||
if (msg.startsWith("CLOCK_JUMPED")) {
|
|
||||||
Long time = parseLongArgument(msg, "TIME");
|
|
||||||
if (time != null && LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.warning("Clock jumped " + time + " seconds");
|
|
||||||
}
|
|
||||||
} else if (msg.startsWith("CLOCK_SKEW")) {
|
|
||||||
Long skew = parseLongArgument(msg, "SKEW");
|
|
||||||
if (skew != null && LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.warning("Clock is skewed by " + skew + " seconds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Long parseLongArgument(String msg, String argName) {
|
|
||||||
String[] args = msg.split(" ");
|
|
||||||
for (String arg : args) {
|
|
||||||
if (arg.startsWith(argName + "=")) {
|
|
||||||
try {
|
|
||||||
return Long.parseLong(arg.substring(argName.length() + 1));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.warning("Failed to parse " + argName + " from '" + msg + "'");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof SettingsUpdatedEvent) {
|
||||||
@@ -886,7 +483,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private void updateConnectionStatus(NetworkStatus status,
|
private void updateConnectionStatus(NetworkStatus status,
|
||||||
boolean charging) {
|
boolean charging) {
|
||||||
connectionStatusExecutor.execute(() -> {
|
connectionStatusExecutor.execute(() -> {
|
||||||
if (!state.isTorRunning()) return;
|
if (!tor.isTorRunning()) return;
|
||||||
boolean online = status.isConnected();
|
boolean online = status.isConnected();
|
||||||
boolean wifi = status.isWifi();
|
boolean wifi = status.isWifi();
|
||||||
boolean ipv6Only = status.isIpv6Only();
|
boolean ipv6Only = status.isIpv6Only();
|
||||||
@@ -970,41 +567,22 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
try {
|
try {
|
||||||
if (enableNetwork) {
|
if (enableNetwork) {
|
||||||
enableBridges(bridgeTypes, country);
|
enableBridges(bridgeTypes, country);
|
||||||
enableConnectionPadding(enableConnectionPadding);
|
tor.enableConnectionPadding(enableConnectionPadding);
|
||||||
enableIpv6(ipv6Only);
|
tor.enableIpv6(ipv6Only);
|
||||||
}
|
}
|
||||||
enableNetwork(enableNetwork);
|
tor.enableNetwork(enableNetwork);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableConnectionPadding(boolean enable) throws IOException {
|
|
||||||
if (!state.enableConnectionPadding(enable)) return; // Unchanged
|
|
||||||
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableIpv6(boolean enable) throws IOException {
|
|
||||||
if (!state.enableIpv6(enable)) return; // Unchanged
|
|
||||||
controlConnection.setConf("ClientUseIPv4", enable ? "0" : "1");
|
|
||||||
controlConnection.setConf("ClientUseIPv6", enable ? "1" : "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
protected class PluginState {
|
private class PluginState {
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private boolean started = false,
|
private boolean settingsChecked = false;
|
||||||
stopped = false,
|
|
||||||
networkInitialised = false,
|
|
||||||
networkEnabled = false,
|
|
||||||
paddingEnabled = false,
|
|
||||||
ipv6Enabled = false,
|
|
||||||
bootstrapped = false,
|
|
||||||
circuitBuilt = false,
|
|
||||||
settingsChecked = false;
|
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private int reasonsDisabled = 0;
|
private int reasonsDisabled = 0;
|
||||||
@@ -1013,84 +591,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private ServerSocket serverSocket = null;
|
private ServerSocket serverSocket = null;
|
||||||
|
|
||||||
@GuardedBy("this")
|
|
||||||
private int orConnectionsConnected = 0;
|
|
||||||
|
|
||||||
@GuardedBy("this")
|
|
||||||
private List<BridgeType> bridgeTypes = emptyList();
|
|
||||||
|
|
||||||
private synchronized void setStarted() {
|
|
||||||
started = true;
|
|
||||||
callback.pluginStateChanged(getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
private synchronized boolean isTorRunning() {
|
|
||||||
return started && !stopped;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private synchronized ServerSocket setStopped() {
|
private synchronized ServerSocket setStopped() {
|
||||||
stopped = true;
|
|
||||||
ServerSocket ss = serverSocket;
|
ServerSocket ss = serverSocket;
|
||||||
serverSocket = null;
|
serverSocket = null;
|
||||||
callback.pluginStateChanged(getState());
|
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void setBootstrapped() {
|
|
||||||
boolean wasBootstrapped = bootstrapped;
|
|
||||||
bootstrapped = true;
|
|
||||||
if (!wasBootstrapped) callback.pluginStateChanged(getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the `circuitBuilt` flag and returns true if the flag has
|
|
||||||
* changed.
|
|
||||||
*/
|
|
||||||
private synchronized boolean setCircuitBuilt(boolean built) {
|
|
||||||
if (built == circuitBuilt) return false; // Unchanged
|
|
||||||
circuitBuilt = built;
|
|
||||||
callback.pluginStateChanged(getState());
|
|
||||||
return true; // Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the `networkEnabled` flag and returns true if the flag has
|
|
||||||
* changed.
|
|
||||||
*/
|
|
||||||
private synchronized boolean enableNetwork(boolean enable) {
|
|
||||||
boolean wasInitialised = networkInitialised;
|
|
||||||
boolean wasEnabled = networkEnabled;
|
|
||||||
networkInitialised = true;
|
|
||||||
networkEnabled = enable;
|
|
||||||
if (!enable) circuitBuilt = false;
|
|
||||||
if (!wasInitialised || enable != wasEnabled) {
|
|
||||||
callback.pluginStateChanged(getState());
|
|
||||||
}
|
|
||||||
return enable != wasEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the `paddingEnabled` flag and returns true if the flag has
|
|
||||||
* changed. Doesn't affect getState().
|
|
||||||
*/
|
|
||||||
private synchronized boolean enableConnectionPadding(boolean enable) {
|
|
||||||
if (enable == paddingEnabled) return false; // Unchanged
|
|
||||||
paddingEnabled = enable;
|
|
||||||
return true; // Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the `ipv6Enabled` flag and returns true if the flag has
|
|
||||||
* changed. Doesn't affect getState().
|
|
||||||
*/
|
|
||||||
private synchronized boolean enableIpv6(boolean enable) {
|
|
||||||
if (enable == ipv6Enabled) return false; // Unchanged
|
|
||||||
ipv6Enabled = enable;
|
|
||||||
return true; // Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void setReasonsDisabled(int reasons) {
|
private synchronized void setReasonsDisabled(int reasons) {
|
||||||
boolean wasChecked = settingsChecked;
|
boolean wasChecked = settingsChecked;
|
||||||
settingsChecked = true;
|
settingsChecked = true;
|
||||||
@@ -1103,7 +610,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
// Doesn't affect getState()
|
// Doesn't affect getState()
|
||||||
private synchronized boolean setServerSocket(ServerSocket ss) {
|
private synchronized boolean setServerSocket(ServerSocket ss) {
|
||||||
if (stopped || serverSocket != null) return false;
|
if (serverSocket != null || !tor.isTorRunning()) return false;
|
||||||
serverSocket = ss;
|
serverSocket = ss;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1113,57 +620,22 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (serverSocket == ss) serverSocket = null;
|
if (serverSocket == ss) serverSocket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private synchronized State getState() {
|
||||||
* Sets the list of bridge types being used and returns true if the
|
return getState(tor.getTorState());
|
||||||
* list has changed. The list is empty if bridges are disabled.
|
|
||||||
* Doesn't affect getState().
|
|
||||||
*/
|
|
||||||
private synchronized boolean setBridgeTypes(List<BridgeType> types) {
|
|
||||||
if (types.equals(bridgeTypes)) return false; // Unchanged
|
|
||||||
bridgeTypes = types;
|
|
||||||
return true; // Changed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized State getState() {
|
private synchronized State getState(TorState torState) {
|
||||||
if (!started || stopped || !settingsChecked) {
|
if (torState == TorState.STARTING_STOPPING || !settingsChecked) {
|
||||||
return STARTING_STOPPING;
|
return STARTING_STOPPING;
|
||||||
}
|
}
|
||||||
if (reasonsDisabled != 0) return DISABLED;
|
if (reasonsDisabled != 0) return DISABLED;
|
||||||
if (!networkInitialised) return ENABLING;
|
if (torState == TorState.CONNECTING) return ENABLING;
|
||||||
if (!networkEnabled) return INACTIVE;
|
if (torState == TorState.CONNECTED) return ACTIVE;
|
||||||
return bootstrapped && circuitBuilt && orConnectionsConnected > 0
|
return INACTIVE;
|
||||||
? ACTIVE : ENABLING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized int getReasonsDisabled() {
|
private synchronized int getReasonsDisabled() {
|
||||||
return getState() == DISABLED ? reasonsDisabled : 0;
|
return getState() == DISABLED ? reasonsDisabled : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void onOrConnectionConnected() {
|
|
||||||
int oldConnected = orConnectionsConnected;
|
|
||||||
orConnectionsConnected++;
|
|
||||||
logOrConnections();
|
|
||||||
if (oldConnected == 0) callback.pluginStateChanged(getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void onOrConnectionClosed() {
|
|
||||||
int oldConnected = orConnectionsConnected;
|
|
||||||
orConnectionsConnected--;
|
|
||||||
if (orConnectionsConnected < 0) {
|
|
||||||
LOG.warning("Count was zero before connection closed");
|
|
||||||
orConnectionsConnected = 0;
|
|
||||||
}
|
|
||||||
logOrConnections();
|
|
||||||
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
|
||||||
callback.pluginStateChanged(getState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("this")
|
|
||||||
private void logOrConnections() {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info(orConnectionsConnected + " OR connections connected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
@@ -17,9 +18,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -45,13 +46,12 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||||
private static final double BACKOFF_BASE = 1.2;
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
|
|
||||||
protected final Executor ioExecutor, wakefulIoExecutor;
|
protected final Executor ioExecutor, eventExecutor, wakefulIoExecutor;
|
||||||
protected final NetworkManager networkManager;
|
protected final NetworkManager networkManager;
|
||||||
protected final LocationUtils locationUtils;
|
protected final LocationUtils locationUtils;
|
||||||
protected final EventBus eventBus;
|
protected final EventBus eventBus;
|
||||||
protected final SocketFactory torSocketFactory;
|
protected final SocketFactory torSocketFactory;
|
||||||
protected final BackoffFactory backoffFactory;
|
protected final BackoffFactory backoffFactory;
|
||||||
protected final ResourceProvider resourceProvider;
|
|
||||||
protected final CircumventionProvider circumventionProvider;
|
protected final CircumventionProvider circumventionProvider;
|
||||||
protected final BatteryManager batteryManager;
|
protected final BatteryManager batteryManager;
|
||||||
protected final Clock clock;
|
protected final Clock clock;
|
||||||
@@ -61,13 +61,13 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
protected final int torControlPort;
|
protected final int torControlPort;
|
||||||
|
|
||||||
TorPluginFactory(@IoExecutor Executor ioExecutor,
|
TorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@EventExecutor Executor eventExecutor,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
BackoffFactory backoffFactory,
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
@@ -76,13 +76,13 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort) {
|
@TorControlPort int torControlPort) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.eventExecutor = eventExecutor;
|
||||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||||
this.networkManager = networkManager;
|
this.networkManager = networkManager;
|
||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.torSocketFactory = torSocketFactory;
|
this.torSocketFactory = torSocketFactory;
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
this.resourceProvider = resourceProvider;
|
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
// Retrieve and parse the latest local properties
|
// Retrieve and parse the latest local properties
|
||||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||||
BdfList message = clientHelper.getMessageAsList(txn,
|
BdfList message = clientHelper.getMessageAsList(txn,
|
||||||
e.getValue().messageId);
|
e.getValue().messageId, false);
|
||||||
local.put(e.getKey(), parseProperties(message));
|
local.put(e.getKey(), parseProperties(message));
|
||||||
}
|
}
|
||||||
return local;
|
return local;
|
||||||
@@ -222,7 +222,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
if (latest != null) {
|
if (latest != null) {
|
||||||
// Retrieve and parse the latest local properties
|
// Retrieve and parse the latest local properties
|
||||||
BdfList message = clientHelper.getMessageAsList(txn,
|
BdfList message = clientHelper.getMessageAsList(txn,
|
||||||
latest.messageId);
|
latest.messageId, false);
|
||||||
p = parseProperties(message);
|
p = parseProperties(message);
|
||||||
}
|
}
|
||||||
return p == null ? new TransportProperties() : p;
|
return p == null ? new TransportProperties() : p;
|
||||||
@@ -252,7 +252,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
local = new TransportProperties();
|
local = new TransportProperties();
|
||||||
} else {
|
} else {
|
||||||
BdfList message = clientHelper.getMessageAsList(txn,
|
BdfList message = clientHelper.getMessageAsList(txn,
|
||||||
latest.messageId);
|
latest.messageId, false);
|
||||||
local = parseProperties(message);
|
local = parseProperties(message);
|
||||||
}
|
}
|
||||||
storeLocalProperties(txn, c, t, local);
|
storeLocalProperties(txn, c, t, local);
|
||||||
@@ -272,8 +272,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
remote = new TransportProperties();
|
remote = new TransportProperties();
|
||||||
} else {
|
} else {
|
||||||
// Retrieve and parse the latest remote properties
|
// Retrieve and parse the latest remote properties
|
||||||
BdfList message =
|
BdfList message = clientHelper.getMessageAsList(txn,
|
||||||
clientHelper.getMessageAsList(txn, latest.messageId);
|
latest.messageId, false);
|
||||||
remote = parseProperties(message);
|
remote = parseProperties(message);
|
||||||
}
|
}
|
||||||
// Merge in any discovered properties
|
// Merge in any discovered properties
|
||||||
@@ -317,7 +317,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
changed = true;
|
changed = true;
|
||||||
} else {
|
} else {
|
||||||
BdfList message = clientHelper.getMessageAsList(txn,
|
BdfList message = clientHelper.getMessageAsList(txn,
|
||||||
latest.messageId);
|
latest.messageId, false);
|
||||||
TransportProperties old = parseProperties(message);
|
TransportProperties old = parseProperties(message);
|
||||||
merged = new TransportProperties(old);
|
merged = new TransportProperties(old);
|
||||||
for (Entry<String, String> e : p.entrySet()) {
|
for (Entry<String, String> e : p.entrySet()) {
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ class TransportPropertyValidator extends BdfMessageValidator {
|
|||||||
|
|
||||||
TransportPropertyValidator(ClientHelper clientHelper,
|
TransportPropertyValidator(ClientHelper clientHelper,
|
||||||
MetadataEncoder metadataEncoder, Clock clock) {
|
MetadataEncoder metadataEncoder, Clock clock) {
|
||||||
super(clientHelper, metadataEncoder, clock);
|
// Accept transport properties in non-canonical form
|
||||||
|
// TODO: Remove this after a reasonable migration period
|
||||||
|
// (added 2023-02-17)
|
||||||
|
super(clientHelper, metadataEncoder, clock, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
|||||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
||||||
@@ -126,6 +127,8 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
|||||||
byte[] payload = nextRecord.getPayload();
|
byte[] payload = nextRecord.getPayload();
|
||||||
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
|
if (payload.length > MAX_MESSAGE_LENGTH)
|
||||||
|
throw new FormatException();
|
||||||
// Validate timestamp
|
// Validate timestamp
|
||||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||||
if (timestamp < 0) throw new FormatException();
|
if (timestamp < 0) throw new FormatException();
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ class SessionParserImpl implements SessionParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Session parseSession(BdfDictionary meta) throws FormatException {
|
public Session parseSession(BdfDictionary meta) throws FormatException {
|
||||||
State state =
|
State state = State.fromValue(meta.getInt(SESSION_KEY_STATE));
|
||||||
State.fromValue(meta.getLong(SESSION_KEY_STATE).intValue());
|
|
||||||
|
|
||||||
MessageId lastLocalMessageId = null;
|
MessageId lastLocalMessageId = null;
|
||||||
byte[] lastLocalMessageIdBytes =
|
byte[] lastLocalMessageIdBytes =
|
||||||
@@ -56,9 +55,9 @@ class SessionParserImpl implements SessionParser {
|
|||||||
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
||||||
|
|
||||||
KeySetId keySetId = null;
|
KeySetId keySetId = null;
|
||||||
Long keySetIdLong = meta.getOptionalLong(SESSION_KEY_KEY_SET_ID);
|
Integer keySetIdInt = meta.getOptionalInt(SESSION_KEY_KEY_SET_ID);
|
||||||
if (keySetIdLong != null) {
|
if (keySetIdInt != null) {
|
||||||
keySetId = new KeySetId(keySetIdLong.intValue());
|
keySetId = new KeySetId(keySetIdInt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Session(state, lastLocalMessageId, localKeyPair,
|
return new Session(state, lastLocalMessageId, localKeyPair,
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ class TransportKeyAgreementManagerImpl extends BdfIncomingMessageHook
|
|||||||
protected DeliveryAction incomingMessage(Transaction txn, Message m,
|
protected DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||||
BdfList body, BdfDictionary meta)
|
BdfList body, BdfDictionary meta)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
MessageType type = MessageType.fromValue(
|
MessageType type =
|
||||||
meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
|
MessageType.fromValue(meta.getInt(MSG_KEY_MESSAGE_TYPE));
|
||||||
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
|
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Received " + type + " message for " + t);
|
LOG.info("Received " + type + " message for " + t);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class TransportKeyAgreementValidator extends BdfMessageValidator {
|
|||||||
@Override
|
@Override
|
||||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||||
BdfList body) throws FormatException {
|
BdfList body) throws FormatException {
|
||||||
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
|
MessageType type = MessageType.fromValue(body.getInt(0));
|
||||||
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
|
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
|
||||||
else if (type == ACTIVATE) return validateActivateMessage(body);
|
else if (type == ACTIVATE) return validateActivateMessage(body);
|
||||||
else throw new AssertionError();
|
else throw new AssertionError();
|
||||||
|
|||||||
@@ -301,8 +301,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
BdfList cv = body.getList(i);
|
BdfList cv = body.getList(i);
|
||||||
ClientId clientId = new ClientId(cv.getString(0));
|
ClientId clientId = new ClientId(cv.getString(0));
|
||||||
int majorVersion = cv.getLong(1).intValue();
|
int majorVersion = cv.getInt(1);
|
||||||
int minorVersion = cv.getLong(2).intValue();
|
int minorVersion = cv.getInt(2);
|
||||||
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion));
|
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion));
|
||||||
}
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
@@ -408,8 +408,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
throws FormatException {
|
throws FormatException {
|
||||||
// Client ID, major version, minor version, active
|
// Client ID, major version, minor version, active
|
||||||
ClientId clientId = new ClientId(clientState.getString(0));
|
ClientId clientId = new ClientId(clientState.getString(0));
|
||||||
int majorVersion = clientState.getLong(1).intValue();
|
int majorVersion = clientState.getInt(1);
|
||||||
int minorVersion = clientState.getLong(2).intValue();
|
int minorVersion = clientState.getInt(2);
|
||||||
boolean active = clientState.getBoolean(3);
|
boolean active = clientState.getBoolean(3);
|
||||||
return new ClientState(clientId, majorVersion, minorVersion, active);
|
return new ClientState(clientId, majorVersion, minorVersion, active);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ class ClientVersioningValidator extends BdfMessageValidator {
|
|||||||
checkSize(clientState, 4);
|
checkSize(clientState, 4);
|
||||||
String clientId = clientState.getString(0);
|
String clientId = clientState.getString(0);
|
||||||
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
|
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
|
||||||
int majorVersion = clientState.getLong(1).intValue();
|
int majorVersion = clientState.getInt(1);
|
||||||
if (majorVersion < 0) throw new FormatException();
|
if (majorVersion < 0) throw new FormatException();
|
||||||
int minorVersion = clientState.getLong(2).intValue();
|
int minorVersion = clientState.getInt(2);
|
||||||
if (minorVersion < 0) throw new FormatException();
|
if (minorVersion < 0) throw new FormatException();
|
||||||
clientState.getBoolean(3);
|
clientState.getBoolean(3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
package org.briarproject.onionwrapper;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
d Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
|
|
||||||
d Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
|
|
||||||
d Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
|
|
||||||
d Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
|
|
||||||
d Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
|
|
||||||
d Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
|
|
||||||
d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
|
|
||||||
d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
|
|
||||||
d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
|
|
||||||
d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
|
|
||||||
d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
|
|
||||||
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
|
||||||
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
|
||||||
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
|
||||||
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
|
||||||
n Bridge obfs4 104.168.68.90:443 ED55B3C321E44EA7E50EF568C8A63CF75E89A58C cert=fgonxDvltTp8nmcOE9sUG94eOAALxETVVXAwnTZJLPpf7rjPuTp+abKl4VyFkxfcLRr5KQ iat-mode=0
|
|
||||||
n Bridge obfs4 45.142.181.131:42069 6EBCF6B02DA2B982F4080A7119D737366AFB74FA cert=9HyWH/BCwWzNirZdTQtluCgJk+gFhpOqydIuyQ1iDvpnqsAynKF+zcPE/INZFefm86UlBg iat-mode=0
|
|
||||||
n Bridge obfs4 85.214.28.204:47111 78A36E46BB082A471848239D3F4390A8F8C6084D cert=96sr3eaUFBDu4wFVAQIfNFElh0UNuutJ/3/Fh2Vu2PHfacQ8bwfY02bwG351U8BZpLnfUQ iat-mode=0
|
|
||||||
n Bridge obfs4 152.67.77.101:4096 B82DB9CDDF887AB8A859420E07DF298E30AF8A6E cert=21OWn3yFo+hulmQNAOtF5uwwOqWtdT5PrLhk8BG9DpOd0/k5DEkQEYPyDdXbS9nZ0E5BJA iat-mode=0
|
|
||||||
n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=nOZ/SaRE3L1dChvjfe0Ks/wM/F8iFhwd3g2G5zgtcLB8x+wiZRWCwjRrbbiQyb3Gh2mxRQ iat-mode=0
|
|
||||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
|
||||||
n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0
|
|
||||||
n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0
|
|
||||||
n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0
|
|
||||||
n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0
|
|
||||||
n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0
|
|
||||||
n Bridge obfs4 45.150.172.16:80 82849E69CBB25EA7F479155F7DCD89D85717FF47 cert=+Krdu1jmVQOxWkMj0mqJHgwbQV49eyD6mZDS+mRExssWNHosa60g4P5Gp81sBJKzN8NrSw iat-mode=0
|
|
||||||
n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0
|
|
||||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
|
||||||
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
|
||||||
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
|
||||||
v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4
|
|
||||||
v Bridge 87.100.193.2:9010 13FB63452AADFA082BD2BC3E1E320AD301F07877
|
|
||||||
v Bridge 65.21.240.163:33245 20BD59649212CFE7412BFC9B94C3CCCFD8F807A8
|
|
||||||
m Bridge meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com utls=hellochrome_auto
|
|
||||||
s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ZZ 1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto
|
|
||||||
ZZ 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto
|
|
||||||
TM 1 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto
|
|
||||||
TM 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto
|
|
||||||
@@ -56,7 +56,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
|
will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
|
||||||
oneOf(clientHelper).toList(message.getBody());
|
oneOf(clientHelper).toList(message, true);
|
||||||
will(returnValue(body));
|
will(returnValue(body));
|
||||||
oneOf(metadataEncoder).encode(dictionary);
|
oneOf(metadataEncoder).encode(dictionary);
|
||||||
will(returnValue(meta));
|
will(returnValue(meta));
|
||||||
@@ -86,7 +86,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timestamp));
|
will(returnValue(timestamp));
|
||||||
oneOf(clientHelper).toList(shortMessage.getBody());
|
oneOf(clientHelper).toList(shortMessage, true);
|
||||||
will(returnValue(body));
|
will(returnValue(body));
|
||||||
oneOf(metadataEncoder).encode(dictionary);
|
oneOf(metadataEncoder).encode(dictionary);
|
||||||
will(returnValue(meta));
|
will(returnValue(meta));
|
||||||
@@ -114,7 +114,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timestamp));
|
will(returnValue(timestamp));
|
||||||
oneOf(clientHelper).toList(message.getBody());
|
oneOf(clientHelper).toList(message, true);
|
||||||
will(throwException(new FormatException()));
|
will(throwException(new FormatException()));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clock).currentTimeMillis();
|
oneOf(clock).currentTimeMillis();
|
||||||
will(returnValue(timestamp));
|
will(returnValue(timestamp));
|
||||||
oneOf(clientHelper).toList(message.getBody());
|
oneOf(clientHelper).toList(message, true);
|
||||||
will(returnValue(body));
|
will(returnValue(body));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory)
|
oneOf(bdfReaderFactory)
|
||||||
.createReader(with(any(InputStream.class)));
|
.createReader(with(any(InputStream.class)), with(true));
|
||||||
will(returnValue(bdfReader));
|
will(returnValue(bdfReader));
|
||||||
oneOf(bdfReader).readList();
|
oneOf(bdfReader).readList();
|
||||||
will(returnValue(list));
|
will(returnValue(list));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.data;
|
package org.briarproject.bramble.data;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -31,11 +32,14 @@ public class BdfReaderImplFuzzingTest extends BrambleTestCase {
|
|||||||
buf[1] = 0x14; // Length 20 bytes
|
buf[1] = 0x14; // Length 20 bytes
|
||||||
in.reset();
|
in.reset();
|
||||||
BdfReaderImpl r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
BdfReaderImpl r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||||
DEFAULT_MAX_BUFFER_SIZE);
|
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||||
int length = r.readString().length();
|
try {
|
||||||
assertTrue(length >= 0);
|
int length = r.readString().length();
|
||||||
assertTrue(length <= 20);
|
assertTrue(length <= 20);
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
|
} catch (FormatException e) {
|
||||||
|
// Expected when bytes are not valid UTF-8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||||
import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT;
|
import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
@@ -88,6 +89,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong16CouldHaveBeenLong8Max() throws Exception {
|
||||||
|
setContents("22" + "007F");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong16CouldHaveBeenLong8Min() throws Exception {
|
||||||
|
setContents("22" + "FF80");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipLong16() throws Exception {
|
public void testSkipLong16() throws Exception {
|
||||||
setContents("22" + "0080");
|
setContents("22" + "0080");
|
||||||
@@ -106,6 +119,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong32CouldHaveBeenLong16Max() throws Exception {
|
||||||
|
setContents("24" + "00007FFF");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong32CouldHaveBeenLong16Min() throws Exception {
|
||||||
|
setContents("24" + "FFFF8000");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipLong32() throws Exception {
|
public void testSkipLong32() throws Exception {
|
||||||
setContents("24" + "00008000");
|
setContents("24" + "00008000");
|
||||||
@@ -124,13 +149,48 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong64CouldHaveBeenLong32Max() throws Exception {
|
||||||
|
setContents("28" + "000000007FFFFFFF");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadLong64CouldHaveBeenLong32Min() throws Exception {
|
||||||
|
setContents("28" + "FFFFFFFF80000000");
|
||||||
|
r.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipLong() throws Exception {
|
public void testSkipLong64() throws Exception {
|
||||||
setContents("28" + "0000000080000000");
|
setContents("28" + "0000000080000000");
|
||||||
r.skipLong();
|
r.skipLong();
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadInt() throws Exception {
|
||||||
|
setContents("21" + "7F" + "21" + "80"
|
||||||
|
+ "22" + "7FFF" + "22" + "8000"
|
||||||
|
+ "24" + "7FFFFFFF" + "24" + "80000000");
|
||||||
|
assertEquals(Byte.MAX_VALUE, r.readInt());
|
||||||
|
assertEquals(Byte.MIN_VALUE, r.readInt());
|
||||||
|
assertEquals(Short.MAX_VALUE, r.readInt());
|
||||||
|
assertEquals(Short.MIN_VALUE, r.readInt());
|
||||||
|
assertEquals(Integer.MAX_VALUE, r.readInt());
|
||||||
|
assertEquals(Integer.MIN_VALUE, r.readInt());
|
||||||
|
assertTrue(r.eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipInt() throws Exception {
|
||||||
|
setContents("21" + "7F" + "22" + "7FFF" + "24" + "7FFFFFFF");
|
||||||
|
r.skipInt();
|
||||||
|
r.skipInt();
|
||||||
|
r.skipInt();
|
||||||
|
assertTrue(r.eof());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDouble() throws Exception {
|
public void testReadDouble() throws Exception {
|
||||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||||
@@ -162,7 +222,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadString8() throws Exception {
|
public void testReadString8() throws Exception {
|
||||||
String longest = getRandomString(Byte.MAX_VALUE);
|
String longest = getRandomString(Byte.MAX_VALUE);
|
||||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
// "foo", the empty string, and 127 random letters
|
// "foo", the empty string, and 127 random letters
|
||||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||||
"41" + "7F" + longHex);
|
"41" + "7F" + longHex);
|
||||||
@@ -186,7 +246,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSkipString8() throws Exception {
|
public void testSkipString8() throws Exception {
|
||||||
String longest = getRandomString(Byte.MAX_VALUE);
|
String longest = getRandomString(Byte.MAX_VALUE);
|
||||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
// "foo", the empty string, and 127 random letters
|
// "foo", the empty string, and 127 random letters
|
||||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||||
"41" + "7F" + longHex);
|
"41" + "7F" + longHex);
|
||||||
@@ -199,9 +259,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadString16() throws Exception {
|
public void testReadString16() throws Exception {
|
||||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||||
String longest = getRandomString(Short.MAX_VALUE);
|
String longest = getRandomString(Short.MAX_VALUE);
|
||||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
// 128 random letters and 2^15 -1 random letters
|
// 128 random letters and 2^15 -1 random letters
|
||||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||||
assertEquals(shortest, r.readString());
|
assertEquals(shortest, r.readString());
|
||||||
@@ -213,7 +273,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
public void testReadString16ChecksMaxLength() throws Exception {
|
public void testReadString16ChecksMaxLength() throws Exception {
|
||||||
int maxBufferSize = Byte.MAX_VALUE + 1;
|
int maxBufferSize = Byte.MAX_VALUE + 1;
|
||||||
String valid = getRandomString(Byte.MAX_VALUE + 1);
|
String valid = getRandomString(Byte.MAX_VALUE + 1);
|
||||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
String validHex = toHexString(valid.getBytes(UTF_8));
|
||||||
String invalidhex = validHex + "20";
|
String invalidhex = validHex + "20";
|
||||||
// 128 random letters, the same plus a space
|
// 128 random letters, the same plus a space
|
||||||
setContents("42" + "0080" + validHex
|
setContents("42" + "0080" + validHex
|
||||||
@@ -223,12 +283,20 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readString();
|
r.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadString16CouldHaveBeenString8() throws Exception {
|
||||||
|
String longest = getRandomString(Byte.MAX_VALUE);
|
||||||
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
|
setContents("42" + "007F" + longHex);
|
||||||
|
r.readString();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipString16() throws Exception {
|
public void testSkipString16() throws Exception {
|
||||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||||
String longest = getRandomString(Short.MAX_VALUE);
|
String longest = getRandomString(Short.MAX_VALUE);
|
||||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
// 128 random letters and 2^15 - 1 random letters
|
// 128 random letters and 2^15 - 1 random letters
|
||||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||||
r.skipString();
|
r.skipString();
|
||||||
@@ -239,7 +307,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadString32() throws Exception {
|
public void testReadString32() throws Exception {
|
||||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||||
// 2^15 random letters
|
// 2^15 random letters
|
||||||
setContents("44" + "00008000" + shortHex);
|
setContents("44" + "00008000" + shortHex);
|
||||||
assertEquals(shortest, r.readString());
|
assertEquals(shortest, r.readString());
|
||||||
@@ -250,7 +318,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
public void testReadString32ChecksMaxLength() throws Exception {
|
public void testReadString32ChecksMaxLength() throws Exception {
|
||||||
int maxBufferSize = Short.MAX_VALUE + 1;
|
int maxBufferSize = Short.MAX_VALUE + 1;
|
||||||
String valid = getRandomString(maxBufferSize);
|
String valid = getRandomString(maxBufferSize);
|
||||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
String validHex = toHexString(valid.getBytes(UTF_8));
|
||||||
String invalidHex = validHex + "20";
|
String invalidHex = validHex + "20";
|
||||||
// 2^15 random letters, the same plus a space
|
// 2^15 random letters, the same plus a space
|
||||||
setContents("44" + "00008000" + validHex +
|
setContents("44" + "00008000" + validHex +
|
||||||
@@ -260,10 +328,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readString();
|
r.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadString32CouldHaveBeenString16() throws Exception {
|
||||||
|
String longest = getRandomString(Short.MAX_VALUE);
|
||||||
|
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||||
|
setContents("44" + "00007FFF" + longHex);
|
||||||
|
r.readString();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipString32() throws Exception {
|
public void testSkipString32() throws Exception {
|
||||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||||
// 2^15 random letters, twice
|
// 2^15 random letters, twice
|
||||||
setContents("44" + "00008000" + shortHex +
|
setContents("44" + "00008000" + shortHex +
|
||||||
"44" + "00008000" + shortHex);
|
"44" + "00008000" + shortHex);
|
||||||
@@ -275,7 +351,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadUtf8String() throws Exception {
|
public void testReadUtf8String() throws Exception {
|
||||||
String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3";
|
String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3";
|
||||||
String hex = toHexString(unicode.getBytes("UTF-8"));
|
String hex = toHexString(unicode.getBytes(UTF_8));
|
||||||
// STRING_8 tag, "foo", the empty string, and the test string
|
// STRING_8 tag, "foo", the empty string, and the test string
|
||||||
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
|
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
|
||||||
assertEquals("foo", r.readString());
|
assertEquals("foo", r.readString());
|
||||||
@@ -348,6 +424,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readRaw();
|
r.readRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadRaw16CouldHaveBeenRaw8() throws Exception {
|
||||||
|
byte[] longest = new byte[Byte.MAX_VALUE];
|
||||||
|
String longHex = toHexString(longest);
|
||||||
|
setContents("52" + "007F" + longHex);
|
||||||
|
r.readRaw();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipRaw16() throws Exception {
|
public void testSkipRaw16() throws Exception {
|
||||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||||
@@ -385,6 +469,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readRaw();
|
r.readRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadRaw32CouldHaveBeenRaw16() throws Exception {
|
||||||
|
byte[] longest = new byte[Short.MAX_VALUE];
|
||||||
|
String longHex = toHexString(longest);
|
||||||
|
setContents("54" + "00007FFF" + longHex);
|
||||||
|
r.readRaw();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipRaw32() throws Exception {
|
public void testSkipRaw32() throws Exception {
|
||||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||||
@@ -434,25 +526,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readList();
|
r.readList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadListManually() throws Exception {
|
|
||||||
// A list containing 1, "foo", and null
|
|
||||||
setContents("60" + "21" + "01" +
|
|
||||||
"41" + "03" + "666F6F" +
|
|
||||||
"00" + "80");
|
|
||||||
r.readListStart();
|
|
||||||
assertFalse(r.hasListEnd());
|
|
||||||
assertEquals(1, r.readLong());
|
|
||||||
assertFalse(r.hasListEnd());
|
|
||||||
assertEquals("foo", r.readString());
|
|
||||||
assertFalse(r.hasListEnd());
|
|
||||||
assertTrue(r.hasNull());
|
|
||||||
r.readNull();
|
|
||||||
assertTrue(r.hasListEnd());
|
|
||||||
r.readListEnd();
|
|
||||||
assertTrue(r.eof());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipList() throws Exception {
|
public void testSkipList() throws Exception {
|
||||||
// A list containing 1, "foo", and 128
|
// A list containing 1, "foo", and 128
|
||||||
@@ -465,9 +538,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDictionary() throws Exception {
|
public void testReadDictionary() throws Exception {
|
||||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
// A dictionary containing "bar" -> null and "foo" -> 123
|
||||||
setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
|
setContents("70" + "41" + "03" + "626172" + "00" +
|
||||||
"41" + "03" + "626172" + "00" + "80");
|
"41" + "03" + "666F6F" + "21" + "7B" + "80");
|
||||||
BdfDictionary dictionary = r.readDictionary();
|
BdfDictionary dictionary = r.readDictionary();
|
||||||
assertEquals(2, dictionary.size());
|
assertEquals(2, dictionary.size());
|
||||||
assertTrue(dictionary.containsKey("foo"));
|
assertTrue(dictionary.containsKey("foo"));
|
||||||
@@ -517,26 +590,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
r.readDictionary();
|
r.readDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadDictionaryManually() throws Exception {
|
|
||||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
|
||||||
setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
|
|
||||||
"41" + "03" + "626172" + "00" + "80");
|
|
||||||
r.readDictionaryStart();
|
|
||||||
assertFalse(r.hasDictionaryEnd());
|
|
||||||
assertEquals("foo", r.readString());
|
|
||||||
assertFalse(r.hasDictionaryEnd());
|
|
||||||
assertEquals(123, r.readLong());
|
|
||||||
assertFalse(r.hasDictionaryEnd());
|
|
||||||
assertEquals("bar", r.readString());
|
|
||||||
assertFalse(r.hasDictionaryEnd());
|
|
||||||
assertTrue(r.hasNull());
|
|
||||||
r.readNull();
|
|
||||||
assertTrue(r.hasDictionaryEnd());
|
|
||||||
r.readDictionaryEnd();
|
|
||||||
assertTrue(r.eof());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipDictionary() throws Exception {
|
public void testSkipDictionary() throws Exception {
|
||||||
// A map containing "foo" -> 123 and "bar" -> null
|
// A map containing "foo" -> 123 and "bar" -> null
|
||||||
@@ -557,10 +610,10 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testNestedListWithinDepthLimit() throws Exception {
|
public void testNestedListWithinDepthLimit() throws Exception {
|
||||||
// A list containing a list containing a list containing a list...
|
// A list containing a list containing a list containing a list...
|
||||||
String lists = "";
|
StringBuilder lists = new StringBuilder();
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "60";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("60");
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "80";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("80");
|
||||||
setContents(lists);
|
setContents(lists.toString());
|
||||||
r.readList();
|
r.readList();
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
@@ -568,23 +621,25 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNestedListOutsideDepthLimit() throws Exception {
|
public void testNestedListOutsideDepthLimit() throws Exception {
|
||||||
// A list containing a list containing a list containing a list...
|
// A list containing a list containing a list containing a list...
|
||||||
String lists = "";
|
StringBuilder lists = new StringBuilder();
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "60";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("60");
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "80";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("80");
|
||||||
setContents(lists);
|
setContents(lists.toString());
|
||||||
r.readList();
|
r.readList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNestedDictionaryWithinDepthLimit() throws Exception {
|
public void testNestedDictionaryWithinDepthLimit() throws Exception {
|
||||||
// A dictionary containing a dictionary containing a dictionary...
|
// A dictionary containing a dictionary containing a dictionary...
|
||||||
String dicts = "";
|
StringBuilder dicts = new StringBuilder();
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||||
dicts += "70" + "41" + "03" + "666F6F";
|
dicts.append("70").append("41").append("03").append("666F6F");
|
||||||
dicts += "11";
|
}
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
dicts.append("11");
|
||||||
dicts += "80";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||||
setContents(dicts);
|
dicts.append("80");
|
||||||
|
}
|
||||||
|
setContents(dicts.toString());
|
||||||
r.readDictionary();
|
r.readDictionary();
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
@@ -592,13 +647,15 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testNestedDictionaryOutsideDepthLimit() throws Exception {
|
public void testNestedDictionaryOutsideDepthLimit() throws Exception {
|
||||||
// A dictionary containing a dictionary containing a dictionary...
|
// A dictionary containing a dictionary containing a dictionary...
|
||||||
String dicts = "";
|
StringBuilder dicts = new StringBuilder();
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||||
dicts += "70" + "41" + "03" + "666F6F";
|
dicts.append("70").append("41").append("03").append("666F6F");
|
||||||
dicts += "11";
|
}
|
||||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
dicts.append("11");
|
||||||
dicts += "80";
|
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||||
setContents(dicts);
|
dicts.append("80");
|
||||||
|
}
|
||||||
|
setContents(dicts.toString());
|
||||||
r.readDictionary();
|
r.readDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,6 +682,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
|||||||
private void setContents(String hex, int maxBufferSize)
|
private void setContents(String hex, int maxBufferSize)
|
||||||
throws FormatException {
|
throws FormatException {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
|
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
|
||||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize);
|
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package org.briarproject.bramble.data;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.data.BdfReader;
|
||||||
|
import org.briarproject.bramble.api.data.BdfWriter;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||||
|
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class BdfReaderWriterIntegrationTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertStringToCanonicalForm() throws Exception {
|
||||||
|
// 'foo' as a STRING_16 (not canonical, should be a STRING_8)
|
||||||
|
String hexIn = "42" + "0003" + "666F6F";
|
||||||
|
InputStream in = new ByteArrayInputStream(fromHexString(hexIn));
|
||||||
|
BdfReader r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||||
|
DEFAULT_MAX_BUFFER_SIZE, false); // Accept non-canonical
|
||||||
|
String s = r.readString();
|
||||||
|
assertEquals("foo", s);
|
||||||
|
assertTrue(r.eof());
|
||||||
|
// Convert the string back to BDF
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
BdfWriter w = new BdfWriterImpl(out);
|
||||||
|
w.writeString(s);
|
||||||
|
w.flush();
|
||||||
|
String hexOut = toHexString(out.toByteArray());
|
||||||
|
// The BDF should now be in canonical form
|
||||||
|
assertEquals("41" + "03" + "666F6F", hexOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertDictionaryToCanonicalForm() throws Exception {
|
||||||
|
// A dictionary with keys in non-canonical order: 'foo' then 'bar'
|
||||||
|
String hexIn = "70" + "41" + "03" + "666F6F" + "21" + "01"
|
||||||
|
+ "41" + "03" + "626172" + "21" + "02" + "80";
|
||||||
|
InputStream in = new ByteArrayInputStream(fromHexString(hexIn));
|
||||||
|
BdfReader r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||||
|
DEFAULT_MAX_BUFFER_SIZE, false); // Accept non-canonical
|
||||||
|
BdfDictionary d = r.readDictionary();
|
||||||
|
assertEquals(2, d.size());
|
||||||
|
assertTrue(r.eof());
|
||||||
|
// The entries should be returned in canonical order
|
||||||
|
Iterator<Entry<String, Object>> it = d.entrySet().iterator();
|
||||||
|
Entry<String, Object> first = it.next();
|
||||||
|
assertEquals("bar", first.getKey());
|
||||||
|
assertEquals(2L, first.getValue());
|
||||||
|
Entry<String, Object> second = it.next();
|
||||||
|
assertEquals("foo", second.getKey());
|
||||||
|
assertEquals(1L, second.getValue());
|
||||||
|
|
||||||
|
// Convert a non-canonical map to BDF (use LinkedHashMap so we know
|
||||||
|
// the entries will be iterated over in non-canonical order)
|
||||||
|
Map<String, Object> m = new LinkedHashMap<>();
|
||||||
|
m.put("foo", 1);
|
||||||
|
m.put("bar", 2);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
BdfWriter w = new BdfWriterImpl(out);
|
||||||
|
w.writeDictionary(m);
|
||||||
|
w.flush();
|
||||||
|
String hexOut = toHexString(out.toByteArray());
|
||||||
|
// The entries should be in canonical order: 'bar' then 'foo'
|
||||||
|
assertEquals("70" + "41" + "03" + "626172" + "21" + "02"
|
||||||
|
+ "41" + "03" + "666F6F" + "21" + "01" + "80", hexOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.data;
|
package org.briarproject.bramble.data;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -168,9 +169,11 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteDictionary() throws IOException {
|
public void testWriteDictionary() throws IOException {
|
||||||
// Use LinkedHashMap to get predictable iteration order
|
// Add entries to dictionary in descending order - they should be
|
||||||
|
// output in ascending order. Use LinkedHashMap to get predictable
|
||||||
|
// iteration order
|
||||||
Map<String, Object> m = new LinkedHashMap<>();
|
Map<String, Object> m = new LinkedHashMap<>();
|
||||||
for (int i = 0; i < 4; i++) m.put(String.valueOf(i), i);
|
for (int i = 3; i >= 0; i--) m.put(String.valueOf(i), i);
|
||||||
w.writeDictionary(m);
|
w.writeDictionary(m);
|
||||||
// DICTIONARY tag, keys as strings and values as integers, END tag
|
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||||
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||||
@@ -180,30 +183,17 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteDelimitedList() throws IOException {
|
public void testWriteBdfDictionary() throws IOException {
|
||||||
w.writeListStart();
|
// Add entries to dictionary in descending order - they should be
|
||||||
w.writeLong(1);
|
// output in ascending order
|
||||||
w.writeString("foo");
|
BdfDictionary d = new BdfDictionary();
|
||||||
w.writeLong(128);
|
for (int i = 3; i >= 0; i--) d.put(String.valueOf(i), i);
|
||||||
w.writeListEnd();
|
w.writeDictionary(d);
|
||||||
// LIST tag, 1 as integer, "foo" as string, 128 as integer, END tag
|
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||||
checkContents("60" + "21" + "01" +
|
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||||
"41" + "03" + "666F6F" +
|
"41" + "01" + "31" + "21" + "01" +
|
||||||
"22" + "0080" + "80");
|
"41" + "01" + "32" + "21" + "02" +
|
||||||
}
|
"41" + "01" + "33" + "21" + "03" + "80");
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteDelimitedDictionary() throws IOException {
|
|
||||||
w.writeDictionaryStart();
|
|
||||||
w.writeString("foo");
|
|
||||||
w.writeLong(123);
|
|
||||||
w.writeString("bar");
|
|
||||||
w.writeNull();
|
|
||||||
w.writeDictionaryEnd();
|
|
||||||
// DICTIONARY tag, "foo" as string, 123 as integer, "bar" as string,
|
|
||||||
// NULL tag, END tag
|
|
||||||
checkContents("70" + "41" + "03" + "666F6F" +
|
|
||||||
"21" + "7B" + "41" + "03" + "626172" + "00" + "80");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
@@ -9,17 +10,23 @@ import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEven
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
|
import org.briarproject.bramble.util.Base32;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.hasEvent;
|
import static org.briarproject.bramble.test.TestUtils.hasEvent;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class MailboxManagerImplTest extends BrambleMockTestCase {
|
public class MailboxManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
@@ -125,4 +132,32 @@ public class MailboxManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertTrue(manager.checkConnection());
|
assertTrue(manager.checkConnection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertBase32Payload() throws FormatException {
|
||||||
|
byte[] payload = getRandomBytes(65);
|
||||||
|
String base32payload = Base32.encode(payload).toLowerCase(Locale.ROOT);
|
||||||
|
String expected = new String(payload, ISO_8859_1);
|
||||||
|
try {
|
||||||
|
manager.convertBase32Payload("foo bar");
|
||||||
|
fail();
|
||||||
|
} catch (FormatException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try { // doesn't work with shorter link
|
||||||
|
manager.convertBase32Payload("briar-mailbox://" +
|
||||||
|
base32payload.substring(0, base32payload.length() - 1));
|
||||||
|
fail();
|
||||||
|
} catch (FormatException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
// works with white-spaces
|
||||||
|
assertEquals(expected, manager.convertBase32Payload(
|
||||||
|
"foo bar briar-mailbox://" + base32payload + " foo bar"));
|
||||||
|
// even works without white-space at the end
|
||||||
|
assertEquals(expected, manager.convertBase32Payload(
|
||||||
|
"foo bar briar-mailbox://" + base32payload + "foobar"));
|
||||||
|
// even works without schema and extra chars at end
|
||||||
|
assertEquals(expected, manager.convertBase32Payload(
|
||||||
|
"foo bar " + base32payload + "foobar"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,13 +217,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
|||||||
// The plugin should have bound a socket and stored the port number
|
// The plugin should have bound a socket and stored the port number
|
||||||
BdfList descriptor = kal.getDescriptor();
|
BdfList descriptor = kal.getDescriptor();
|
||||||
assertEquals(3, descriptor.size());
|
assertEquals(3, descriptor.size());
|
||||||
assertEquals(TRANSPORT_ID_LAN, descriptor.getLong(0).longValue());
|
assertEquals(TRANSPORT_ID_LAN, descriptor.getInt(0).intValue());
|
||||||
byte[] address = descriptor.getRaw(1);
|
byte[] address = descriptor.getRaw(1);
|
||||||
InetAddress addr = InetAddress.getByAddress(address);
|
InetAddress addr = InetAddress.getByAddress(address);
|
||||||
assertTrue(addr instanceof Inet4Address);
|
assertTrue(addr instanceof Inet4Address);
|
||||||
assertFalse(addr.isLoopbackAddress());
|
assertFalse(addr.isLoopbackAddress());
|
||||||
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
|
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
|
||||||
int port = descriptor.getLong(2).intValue();
|
int port = descriptor.getInt(2);
|
||||||
assertTrue(port > 0 && port < 65536);
|
assertTrue(port > 0 && port < 65536);
|
||||||
// The plugin should be listening on the port
|
// The plugin should be listening on the port
|
||||||
InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
|
InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DPI_BRIDGES;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class CircumventionProviderImplTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
private final CircumventionProviderImpl provider =
|
|
||||||
new CircumventionProviderImpl();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvariants() {
|
|
||||||
Set<String> blocked = new HashSet<>(asList(BLOCKED));
|
|
||||||
Set<String> bridges = new HashSet<>(asList(BRIDGES));
|
|
||||||
Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES));
|
|
||||||
Set<String> nonDefaultBridges =
|
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
|
||||||
Set<String> dpiBridges = new HashSet<>(asList(DPI_BRIDGES));
|
|
||||||
// BRIDGES should be a subset of BLOCKED
|
|
||||||
assertTrue(blocked.containsAll(bridges));
|
|
||||||
// BRIDGES should be the union of the bridge type sets
|
|
||||||
Set<String> union = new HashSet<>(defaultBridges);
|
|
||||||
union.addAll(nonDefaultBridges);
|
|
||||||
union.addAll(dpiBridges);
|
|
||||||
assertEquals(bridges, union);
|
|
||||||
// The bridge type sets should not overlap
|
|
||||||
assertEmptyIntersection(defaultBridges, nonDefaultBridges);
|
|
||||||
assertEmptyIntersection(defaultBridges, dpiBridges);
|
|
||||||
assertEmptyIntersection(nonDefaultBridges, dpiBridges);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetBestBridgeType() {
|
|
||||||
for (String country : DEFAULT_BRIDGES) {
|
|
||||||
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
|
||||||
provider.getSuitableBridgeTypes(country));
|
|
||||||
}
|
|
||||||
for (String country : NON_DEFAULT_BRIDGES) {
|
|
||||||
assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA),
|
|
||||||
provider.getSuitableBridgeTypes(country));
|
|
||||||
}
|
|
||||||
for (String country : DPI_BRIDGES) {
|
|
||||||
assertEquals(asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE),
|
|
||||||
provider.getSuitableBridgeTypes(country));
|
|
||||||
}
|
|
||||||
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
|
||||||
provider.getSuitableBridgeTypes("ZZ"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasSnowflakeParamsWithLetsEncrypt() {
|
|
||||||
testHasSnowflakeParams(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasSnowflakeParamsWithoutLetsEncrypt() {
|
|
||||||
testHasSnowflakeParams(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testHasSnowflakeParams(boolean letsEncrypt) {
|
|
||||||
String tmParams = provider.getSnowflakeParams("TM", letsEncrypt);
|
|
||||||
String defaultParams = provider.getSnowflakeParams("ZZ", letsEncrypt);
|
|
||||||
assertFalse(tmParams.isEmpty());
|
|
||||||
assertFalse(defaultParams.isEmpty());
|
|
||||||
assertNotEquals(defaultParams, tmParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void assertEmptyIntersection(Set<T> a, Set<T> b) {
|
|
||||||
Set<T> intersection = new HashSet<>(a);
|
|
||||||
intersection.retainAll(b);
|
|
||||||
assertTrue(intersection.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -404,7 +404,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||||
will(returnValue(fooUpdate));
|
will(returnValue(fooUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
fooPropertiesDict);
|
fooPropertiesDict);
|
||||||
@@ -471,7 +471,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup2.getId());
|
contactGroup2.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||||
will(returnValue(fooUpdate));
|
will(returnValue(fooUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
fooPropertiesDict);
|
fooPropertiesDict);
|
||||||
@@ -526,7 +526,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup.getId());
|
contactGroup.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||||
will(returnValue(update));
|
will(returnValue(update));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
fooPropertiesDict);
|
fooPropertiesDict);
|
||||||
@@ -564,7 +564,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||||
will(returnValue(update));
|
will(returnValue(update));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
fooPropertiesDict);
|
fooPropertiesDict);
|
||||||
@@ -695,7 +695,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(localGroupMessageMetadata));
|
will(returnValue(localGroupMessageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||||
|
false);
|
||||||
will(returnValue(oldUpdate));
|
will(returnValue(oldUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
oldPropertiesDict);
|
oldPropertiesDict);
|
||||||
@@ -760,7 +761,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(localGroupMessageMetadata));
|
will(returnValue(localGroupMessageMetadata));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||||
|
false);
|
||||||
will(returnValue(oldUpdate));
|
will(returnValue(oldUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
oldPropertiesDict);
|
oldPropertiesDict);
|
||||||
@@ -819,12 +821,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
localGroup.getId());
|
localGroup.getId());
|
||||||
will(returnValue(messageMetadata));
|
will(returnValue(messageMetadata));
|
||||||
// Retrieve and parse the latest local properties
|
// Retrieve and parse the latest local properties
|
||||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
oneOf(clientHelper).getMessageAsList(txn, fooVersion999, false);
|
||||||
will(returnValue(fooUpdate));
|
will(returnValue(fooUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
fooPropertiesDict);
|
fooPropertiesDict);
|
||||||
will(returnValue(fooProperties));
|
will(returnValue(fooProperties));
|
||||||
oneOf(clientHelper).getMessageAsList(txn, barVersion3);
|
oneOf(clientHelper).getMessageAsList(txn, barVersion3, false);
|
||||||
will(returnValue(barUpdate));
|
will(returnValue(barUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
barPropertiesDict);
|
barPropertiesDict);
|
||||||
|
|||||||
@@ -6,13 +6,18 @@ import org.briarproject.bramble.api.UniqueId;
|
|||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Priority;
|
import org.briarproject.bramble.api.sync.Priority;
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||||
import org.briarproject.bramble.api.sync.Versions;
|
import org.briarproject.bramble.api.sync.Versions;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.PredicateMatcher;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -23,12 +28,15 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||||
|
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
@@ -46,6 +54,38 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
private final SyncRecordReader reader =
|
private final SyncRecordReader reader =
|
||||||
new SyncRecordReaderImpl(messageFactory, recordReader);
|
new SyncRecordReaderImpl(messageFactory, recordReader);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoFormatExceptionIfMessageIsMinimumSize() throws Exception {
|
||||||
|
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH + 1));
|
||||||
|
expectCreateMessage(1);
|
||||||
|
|
||||||
|
reader.readMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testFormatExceptionIfMessageIsTooSmall() throws Exception {
|
||||||
|
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH));
|
||||||
|
|
||||||
|
reader.readMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoFormatExceptionIfMessageIsMaximumSize() throws Exception {
|
||||||
|
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH
|
||||||
|
+ MAX_MESSAGE_BODY_LENGTH));
|
||||||
|
expectCreateMessage(MAX_MESSAGE_BODY_LENGTH);
|
||||||
|
|
||||||
|
reader.readMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testFormatExceptionIfMessageIsTooLarge() throws Exception {
|
||||||
|
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH
|
||||||
|
+ MAX_MESSAGE_BODY_LENGTH + 1));
|
||||||
|
|
||||||
|
reader.readMessage();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
||||||
expectReadRecord(createAck());
|
expectReadRecord(createAck());
|
||||||
@@ -158,6 +198,20 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
assertTrue(reader.eof());
|
assertTrue(reader.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectCreateMessage(int bodyLength) {
|
||||||
|
MessageId messageId = new MessageId(getRandomId());
|
||||||
|
GroupId groupId = new GroupId(getRandomId());
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
Matcher<byte[]> matcher = new PredicateMatcher<>(byte[].class,
|
||||||
|
b -> b.length == MESSAGE_HEADER_LENGTH + bodyLength);
|
||||||
|
oneOf(messageFactory).createMessage(with(matcher));
|
||||||
|
will(returnValue(new Message(messageId, groupId, timestamp,
|
||||||
|
new byte[bodyLength])));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
private void expectReadRecord(@Nullable Record record) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
@@ -167,6 +221,10 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Record createMessage(int payloadLength) {
|
||||||
|
return new Record(PROTOCOL_VERSION, MESSAGE, new byte[payloadLength]);
|
||||||
|
}
|
||||||
|
|
||||||
private Record createAck() throws Exception {
|
private Record createAck() throws Exception {
|
||||||
return new Record(PROTOCOL_VERSION, ACK, createPayload());
|
return new Record(PROTOCOL_VERSION, ACK, createPayload());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ public class TestFeatureFlagModule {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableMailbox() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldEnablePrivateGroupsInCore() {
|
public boolean shouldEnablePrivateGroupsInCore() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import static java.lang.Double.compare;
|
||||||
|
import static java.lang.Double.parseDouble;
|
||||||
import static org.briarproject.bramble.test.UTest.Result.INCONCLUSIVE;
|
import static org.briarproject.bramble.test.UTest.Result.INCONCLUSIVE;
|
||||||
import static org.briarproject.bramble.test.UTest.Result.LARGER;
|
import static org.briarproject.bramble.test.UTest.Result.LARGER;
|
||||||
import static org.briarproject.bramble.test.UTest.Result.SMALLER;
|
import static org.briarproject.bramble.test.UTest.Result.SMALLER;
|
||||||
@@ -134,7 +136,7 @@ public class UTest {
|
|||||||
if (nA < 5 || nB < 5) die("Too few values for U test\n");
|
if (nA < 5 || nB < 5) die("Too few values for U test\n");
|
||||||
|
|
||||||
double zCritical;
|
double zCritical;
|
||||||
if (args.length == 3) zCritical = Double.valueOf(args[2]);
|
if (args.length == 3) zCritical = parseDouble(args[2]);
|
||||||
else zCritical = Z_CRITICAL_0_01;
|
else zCritical = Z_CRITICAL_0_01;
|
||||||
|
|
||||||
switch (test(a, b, zCritical)) {
|
switch (test(a, b, zCritical)) {
|
||||||
@@ -161,7 +163,7 @@ public class UTest {
|
|||||||
BufferedReader in;
|
BufferedReader in;
|
||||||
in = new BufferedReader(new FileReader(filename));
|
in = new BufferedReader(new FileReader(filename));
|
||||||
String s;
|
String s;
|
||||||
while ((s = in.readLine()) != null) values.add(new Double(s));
|
while ((s = in.readLine()) != null) values.add(parseDouble(s));
|
||||||
in.close();
|
in.close();
|
||||||
} catch (FileNotFoundException fnf) {
|
} catch (FileNotFoundException fnf) {
|
||||||
die(filename + " not found");
|
die(filename + " not found");
|
||||||
@@ -187,9 +189,7 @@ public class UTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@Nonnull Value v) {
|
public int compareTo(@Nonnull Value v) {
|
||||||
if (value < v.value) return -1;
|
return compare(value, v.value);
|
||||||
if (value > v.value) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,51 +88,58 @@ public class StringUtilsTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8() {
|
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8()
|
||||||
|
throws Exception {
|
||||||
// The UTF-8 encoding of the null character is valid
|
// The UTF-8 encoding of the null character is valid
|
||||||
assertEquals("\u0000", StringUtils.fromUtf8(new byte[1]));
|
byte[] utf8 = new byte[1];
|
||||||
|
String actual = StringUtils.fromUtf8(utf8);
|
||||||
|
assertEquals("\u0000", actual);
|
||||||
|
// When we convert back to UTF-8 we should get the original encoding
|
||||||
|
assertArrayEquals(utf8, StringUtils.toUtf8(actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = FormatException.class)
|
||||||
public void testFromUtf8RemovesNullCharacterUsingModifiedUtf8() {
|
public void testFromUtf8RejectsNullCharacterUsingModifiedUtf8()
|
||||||
|
throws Exception {
|
||||||
// The modified UTF-8 encoding of the null character is not valid
|
// The modified UTF-8 encoding of the null character is not valid
|
||||||
byte[] b = new byte[] {
|
byte[] b = new byte[] {
|
||||||
(byte) 0xC0, (byte) 0x80, // Null character as modified UTF-8
|
(byte) 0xC0, (byte) 0x80, // Null character as modified UTF-8
|
||||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||||
};
|
};
|
||||||
// Conversion should ignore the invalid character and return the rest
|
StringUtils.fromUtf8(b);
|
||||||
String expected = "\u0205";
|
|
||||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8() {
|
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8()
|
||||||
|
throws Exception {
|
||||||
// The UTF-8 encoding of a supplementary character is valid and should
|
// The UTF-8 encoding of a supplementary character is valid and should
|
||||||
// be converted to a surrogate pair
|
// be converted to a surrogate pair
|
||||||
byte[] b = new byte[] {
|
byte[] utf8 = new byte[] {
|
||||||
(byte) 0xF0, (byte) 0x90, (byte) 0x90, (byte) 0x80, // U+10400
|
(byte) 0xF0, (byte) 0x90, (byte) 0x90, (byte) 0x80, // U+10400
|
||||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||||
};
|
};
|
||||||
String expected = "\uD801\uDC00\u0205"; // Surrogate pair
|
String expected = "\uD801\uDC00\u0205"; // Surrogate pair
|
||||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
String actual = StringUtils.fromUtf8(utf8);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
// When we convert back to UTF-8 we should get the original encoding
|
||||||
|
assertArrayEquals(utf8, StringUtils.toUtf8(actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = FormatException.class)
|
||||||
public void testFromUtf8RemovesSupplementaryCharacterUsingModifiedUtf8() {
|
public void testFromUtf8RejectsSupplementaryCharacterUsingModifiedUtf8()
|
||||||
|
throws Exception {
|
||||||
// The CESU-8 or modified UTF-8 encoding of a supplementary character
|
// The CESU-8 or modified UTF-8 encoding of a supplementary character
|
||||||
// is not valid
|
// is not valid
|
||||||
byte[] b = new byte[] {
|
byte[] utf8 = new byte[] {
|
||||||
(byte) 0xED, (byte) 0xA0, (byte) 0x81, // U+10400 as CSEU-8
|
(byte) 0xED, (byte) 0xA0, (byte) 0x81, // U+10400 as CSEU-8
|
||||||
(byte) 0xED, (byte) 0xB0, (byte) 0x80,
|
(byte) 0xED, (byte) 0xB0, (byte) 0x80,
|
||||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||||
};
|
};
|
||||||
// Conversion should ignore the invalid character and return the rest
|
StringUtils.fromUtf8(utf8);
|
||||||
String expected = "\u0205";
|
|
||||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFromUtf8EmptyInput() {
|
public void testFromUtf8EmptyInput() throws Exception {
|
||||||
assertEquals("", StringUtils.fromUtf8(new byte[0]));
|
assertEquals("", StringUtils.fromUtf8(new byte[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ dependencyVerification {
|
|||||||
'org.bouncycastle:bcprov-jdk15to18:1.71:bcprov-jdk15to18-1.71.jar:143aaa4a40edd5fc2a18db7900059f6c16f4d931b94b94b20f7e2238e6662886',
|
'org.bouncycastle:bcprov-jdk15to18:1.71:bcprov-jdk15to18-1.71.jar:143aaa4a40edd5fc2a18db7900059f6c16f4d931b94b94b20f7e2238e6662886',
|
||||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
|
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||||
'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
|
'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ apply plugin: 'witness'
|
|||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
apply from: '../dagger.gradle'
|
apply from: '../dagger.gradle'
|
||||||
|
|
||||||
configurations {
|
|
||||||
tor
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':bramble-core')
|
implementation project(':bramble-core')
|
||||||
|
|
||||||
@@ -18,13 +14,7 @@ dependencies {
|
|||||||
def jna_version = '4.5.2'
|
def jna_version = '4.5.2'
|
||||||
implementation "net.java.dev.jna:jna:$jna_version"
|
implementation "net.java.dev.jna:jna:$jna_version"
|
||||||
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
||||||
|
implementation "org.briarproject:onionwrapper-java:$onionwrapper_version"
|
||||||
tor "org.briarproject:tor-linux:$tor_version"
|
|
||||||
tor "org.briarproject:tor-windows:$tor_version"
|
|
||||||
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
|
||||||
tor "org.briarproject:obfs4proxy-windows:$obfs4proxy_version"
|
|
||||||
tor "org.briarproject:snowflake-linux:$snowflake_version"
|
|
||||||
tor "org.briarproject:snowflake-windows:$snowflake_version"
|
|
||||||
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
@@ -34,34 +24,6 @@ dependencies {
|
|||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
testImplementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
|
||||||
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
|
||||||
}
|
|
||||||
|
|
||||||
def torBinariesDir = 'src/main/resources'
|
|
||||||
|
|
||||||
task cleanTorBinaries {
|
|
||||||
doLast {
|
|
||||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clean.dependsOn cleanTorBinaries
|
|
||||||
|
|
||||||
task unpackTorBinaries {
|
|
||||||
doLast {
|
|
||||||
copy {
|
|
||||||
from configurations.tor.collect { zipTree(it) }
|
|
||||||
into torBinariesDir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependsOn cleanTorBinaries
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
inputs.dir torBinariesDir
|
|
||||||
dependsOn unpackTorBinaries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test) {
|
tasks.withType(Test) {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble;
|
|||||||
import org.briarproject.bramble.io.DnsModule;
|
import org.briarproject.bramble.io.DnsModule;
|
||||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||||
import org.briarproject.bramble.network.JavaNetworkModule;
|
import org.briarproject.bramble.network.JavaNetworkModule;
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
|
||||||
import org.briarproject.bramble.socks.SocksModule;
|
import org.briarproject.bramble.socks.SocksModule;
|
||||||
import org.briarproject.bramble.system.JavaSystemModule;
|
import org.briarproject.bramble.system.JavaSystemModule;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.security.CodeSource;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class JavaTorPlugin extends TorPlugin {
|
|
||||||
|
|
||||||
JavaTorPlugin(Executor ioExecutor,
|
|
||||||
Executor wakefulIoExecutor,
|
|
||||||
NetworkManager networkManager,
|
|
||||||
LocationUtils locationUtils,
|
|
||||||
SocketFactory torSocketFactory,
|
|
||||||
Clock clock,
|
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
|
||||||
BatteryManager batteryManager,
|
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
|
||||||
PluginCallback callback,
|
|
||||||
String architecture,
|
|
||||||
long maxLatency,
|
|
||||||
int maxIdleTime,
|
|
||||||
File torDirectory,
|
|
||||||
int torSocksPort,
|
|
||||||
int torControlPort) {
|
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
|
||||||
torSocketFactory, clock, resourceProvider,
|
|
||||||
circumventionProvider, batteryManager, backoff,
|
|
||||||
torRendezvousCrypto, callback, architecture,
|
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
|
||||||
torControlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getLastUpdateTime() {
|
|
||||||
CodeSource codeSource =
|
|
||||||
getClass().getProtectionDomain().getCodeSource();
|
|
||||||
if (codeSource == null) throw new AssertionError("CodeSource null");
|
|
||||||
try {
|
|
||||||
URI path = codeSource.getLocation().toURI();
|
|
||||||
File file = new File(path);
|
|
||||||
return file.lastModified();
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import com.sun.jna.Library;
|
|
||||||
import com.sun.jna.Native;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class UnixTorPlugin extends JavaTorPlugin {
|
|
||||||
|
|
||||||
UnixTorPlugin(Executor ioExecutor,
|
|
||||||
Executor wakefulIoExecutor,
|
|
||||||
NetworkManager networkManager,
|
|
||||||
LocationUtils locationUtils,
|
|
||||||
SocketFactory torSocketFactory,
|
|
||||||
Clock clock,
|
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
|
||||||
BatteryManager batteryManager,
|
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
|
||||||
PluginCallback callback,
|
|
||||||
String architecture,
|
|
||||||
long maxLatency,
|
|
||||||
int maxIdleTime,
|
|
||||||
File torDirectory,
|
|
||||||
int torSocksPort,
|
|
||||||
int torControlPort) {
|
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
|
||||||
torSocketFactory, clock, resourceProvider,
|
|
||||||
circumventionProvider, batteryManager, backoff,
|
|
||||||
torRendezvousCrypto, callback, architecture,
|
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
|
||||||
torControlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getProcessId() {
|
|
||||||
return CLibrary.INSTANCE.getpid();
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface CLibrary extends Library {
|
|
||||||
|
|
||||||
CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class);
|
|
||||||
|
|
||||||
int getpid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
@@ -13,9 +14,11 @@ import org.briarproject.bramble.api.plugin.TorDirectory;
|
|||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.UnixTorWrapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -34,13 +37,13 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UnixTorPluginFactory(@IoExecutor Executor ioExecutor,
|
UnixTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@EventExecutor Executor eventExecutor,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
BackoffFactory backoffFactory,
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
@@ -48,8 +51,8 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
@TorDirectory File torDirectory,
|
@TorDirectory File torDirectory,
|
||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort) {
|
@TorControlPort int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
}
|
}
|
||||||
@@ -62,9 +65,10 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("System's os.arch is " + arch);
|
LOG.info("System's os.arch is " + arch);
|
||||||
}
|
}
|
||||||
if (arch.equals("amd64")) return "linux-x86_64";
|
//noinspection IfCanBeSwitch
|
||||||
else if (arch.equals("aarch64")) return "linux-aarch64";
|
if (arch.equals("amd64")) return "x86_64";
|
||||||
else if (arch.equals("arm")) return "linux-armhf";
|
else if (arch.equals("aarch64")) return "aarch64";
|
||||||
|
else if (arch.equals("arm")) return "armhf";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,11 +76,11 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(Backoff backoff,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
|
TorWrapper tor = new UnixTorWrapper(ioExecutor, eventExecutor,
|
||||||
networkManager, locationUtils, torSocketFactory, clock,
|
architecture, torDirectory, torSocksPort, torControlPort);
|
||||||
resourceProvider, circumventionProvider, batteryManager,
|
return new TorPlugin(ioExecutor, wakefulIoExecutor, networkManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
locationUtils, torSocketFactory, circumventionProvider,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
batteryManager, backoff, torRendezvousCrypto, tor, callback,
|
||||||
torControlPort);
|
MAX_LATENCY, MAX_IDLE_TIME, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import com.sun.jna.platform.win32.Kernel32;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class WindowsTorPlugin extends JavaTorPlugin {
|
|
||||||
|
|
||||||
WindowsTorPlugin(Executor ioExecutor,
|
|
||||||
Executor wakefulIoExecutor,
|
|
||||||
NetworkManager networkManager,
|
|
||||||
LocationUtils locationUtils,
|
|
||||||
SocketFactory torSocketFactory,
|
|
||||||
Clock clock,
|
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
|
||||||
BatteryManager batteryManager,
|
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
|
||||||
PluginCallback callback,
|
|
||||||
String architecture,
|
|
||||||
long maxLatency,
|
|
||||||
int maxIdleTime,
|
|
||||||
File torDirectory,
|
|
||||||
int torSocksPort,
|
|
||||||
int torControlPort) {
|
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
|
||||||
torSocketFactory, clock, resourceProvider,
|
|
||||||
circumventionProvider, batteryManager, backoff,
|
|
||||||
torRendezvousCrypto, callback, architecture,
|
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
|
||||||
torControlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getProcessId() {
|
|
||||||
return Kernel32.INSTANCE.GetCurrentProcessId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void waitForTorToStart(Process torProcess)
|
|
||||||
throws InterruptedException, PluginException {
|
|
||||||
// On Windows the RunAsDaemon option has no effect, so Tor won't detach.
|
|
||||||
// Wait for the control port to be opened, then continue to read its
|
|
||||||
// stdout and stderr in a background thread until it exits.
|
|
||||||
BlockingQueue<Boolean> success = new ArrayBlockingQueue<>(1);
|
|
||||||
ioExecutor.execute(() -> {
|
|
||||||
boolean started = false;
|
|
||||||
// Read the process's stdout (and redirected stderr)
|
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
|
||||||
// Log the first line of stdout (contains Tor and library versions)
|
|
||||||
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
|
||||||
// Startup has succeeded when the control port is open
|
|
||||||
while (stdout.hasNextLine()) {
|
|
||||||
String line = stdout.nextLine();
|
|
||||||
if (!started && line.contains("Opened Control listener")) {
|
|
||||||
success.add(true);
|
|
||||||
started = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stdout.close();
|
|
||||||
// If the control port wasn't opened, startup has failed
|
|
||||||
if (!started) success.add(false);
|
|
||||||
// Wait for the process to exit
|
|
||||||
try {
|
|
||||||
int exit = torProcess.waitFor();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Tor exited with value " + exit);
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
LOG.warning("Interrupted while waiting for Tor to exit");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Wait for the startup result
|
|
||||||
if (!success.take()) throw new PluginException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
@@ -13,9 +14,11 @@ import org.briarproject.bramble.api.plugin.TorDirectory;
|
|||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.WindowsTorWrapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -34,13 +37,13 @@ public class WindowsTorPluginFactory extends TorPluginFactory {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WindowsTorPluginFactory(@IoExecutor Executor ioExecutor,
|
WindowsTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@EventExecutor Executor eventExecutor,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
BackoffFactory backoffFactory,
|
||||||
ResourceProvider resourceProvider,
|
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
@@ -48,8 +51,8 @@ public class WindowsTorPluginFactory extends TorPluginFactory {
|
|||||||
@TorDirectory File torDirectory,
|
@TorDirectory File torDirectory,
|
||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort) {
|
@TorControlPort int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
}
|
}
|
||||||
@@ -62,7 +65,7 @@ public class WindowsTorPluginFactory extends TorPluginFactory {
|
|||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("System's os.arch is " + arch);
|
LOG.info("System's os.arch is " + arch);
|
||||||
}
|
}
|
||||||
if (arch.equals("amd64")) return "windows-x86_64";
|
if (arch.equals("amd64")) return "x86_64";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +73,11 @@ public class WindowsTorPluginFactory extends TorPluginFactory {
|
|||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(Backoff backoff,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new WindowsTorPlugin(ioExecutor, wakefulIoExecutor,
|
TorWrapper tor = new WindowsTorWrapper(ioExecutor, eventExecutor,
|
||||||
networkManager, locationUtils, torSocketFactory, clock,
|
architecture, torDirectory, torSocksPort, torControlPort);
|
||||||
resourceProvider, circumventionProvider, batteryManager,
|
return new TorPlugin(ioExecutor, wakefulIoExecutor, networkManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
locationUtils, torSocketFactory, circumventionProvider,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
batteryManager, backoff, torRendezvousCrypto, tor, callback,
|
||||||
torControlPort);
|
MAX_LATENCY, MAX_IDLE_TIME, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,283 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
|
||||||
import org.briarproject.bramble.api.Multiset;
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
|
|
||||||
import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent;
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent;
|
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class BridgeTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
private static final String[] SNOWFLAKE_COUNTRY_CODES = {"TM", "ZZ"};
|
|
||||||
|
|
||||||
@Parameters
|
|
||||||
public static Iterable<Params> data() {
|
|
||||||
BrambleJavaIntegrationTestComponent component =
|
|
||||||
DaggerBrambleJavaIntegrationTestComponent.builder().build();
|
|
||||||
BrambleCoreIntegrationTestEagerSingletons.Helper
|
|
||||||
.injectEagerSingletons(component);
|
|
||||||
// Share stats among all the test instances
|
|
||||||
Stats stats = new Stats();
|
|
||||||
CircumventionProvider provider = component.getCircumventionProvider();
|
|
||||||
List<Params> states = new ArrayList<>();
|
|
||||||
for (int i = 0; i < ATTEMPTS_PER_BRIDGE; i++) {
|
|
||||||
for (String bridge :
|
|
||||||
provider.getBridges(DEFAULT_OBFS4, "", true)) {
|
|
||||||
states.add(new Params(bridge, DEFAULT_OBFS4, stats, false));
|
|
||||||
}
|
|
||||||
for (String bridge :
|
|
||||||
provider.getBridges(NON_DEFAULT_OBFS4, "", true)) {
|
|
||||||
states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats,
|
|
||||||
false));
|
|
||||||
}
|
|
||||||
for (String bridge : provider.getBridges(VANILLA, "", true)) {
|
|
||||||
states.add(new Params(bridge, VANILLA, stats, false));
|
|
||||||
}
|
|
||||||
for (String bridge : provider.getBridges(MEEK, "", true)) {
|
|
||||||
states.add(new Params(bridge, MEEK, stats, true));
|
|
||||||
}
|
|
||||||
for (String countryCode : SNOWFLAKE_COUNTRY_CODES) {
|
|
||||||
for (String bridge :
|
|
||||||
provider.getBridges(SNOWFLAKE, countryCode, true)) {
|
|
||||||
states.add(new Params(bridge, SNOWFLAKE, stats, true));
|
|
||||||
}
|
|
||||||
for (String bridge :
|
|
||||||
provider.getBridges(SNOWFLAKE, countryCode, false)) {
|
|
||||||
states.add(new Params(bridge, SNOWFLAKE, stats, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return states;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static long TIMEOUT = MINUTES.toMillis(2);
|
|
||||||
private final static long MEEK_TIMEOUT = MINUTES.toMillis(6);
|
|
||||||
private final static int UNREACHABLE_BRIDGES_ALLOWED = 6;
|
|
||||||
private final static int ATTEMPTS_PER_BRIDGE = 5;
|
|
||||||
|
|
||||||
private final static Logger LOG = getLogger(BridgeTest.class.getName());
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@IoExecutor
|
|
||||||
Executor ioExecutor;
|
|
||||||
@Inject
|
|
||||||
@WakefulIoExecutor
|
|
||||||
Executor wakefulIoExecutor;
|
|
||||||
@Inject
|
|
||||||
NetworkManager networkManager;
|
|
||||||
@Inject
|
|
||||||
ResourceProvider resourceProvider;
|
|
||||||
@Inject
|
|
||||||
BatteryManager batteryManager;
|
|
||||||
@Inject
|
|
||||||
EventBus eventBus;
|
|
||||||
@Inject
|
|
||||||
BackoffFactory backoffFactory;
|
|
||||||
@Inject
|
|
||||||
Clock clock;
|
|
||||||
@Inject
|
|
||||||
CryptoComponent crypto;
|
|
||||||
@Inject
|
|
||||||
@TorSocksPort
|
|
||||||
int torSocksPort;
|
|
||||||
@Inject
|
|
||||||
@TorControlPort
|
|
||||||
int torControlPort;
|
|
||||||
|
|
||||||
private final File torDir = getTestDirectory();
|
|
||||||
private final Params params;
|
|
||||||
|
|
||||||
private UnixTorPluginFactory factory;
|
|
||||||
|
|
||||||
public BridgeTest(Params params) {
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
// Skip this test unless it's explicitly enabled in the environment
|
|
||||||
assumeTrue(isOptionalTestEnabled(BridgeTest.class));
|
|
||||||
|
|
||||||
// TODO: Remove this assumption when the plugin supports other platforms
|
|
||||||
assumeTrue(OsUtils.isLinux());
|
|
||||||
|
|
||||||
BrambleJavaIntegrationTestComponent component =
|
|
||||||
DaggerBrambleJavaIntegrationTestComponent.builder().build();
|
|
||||||
BrambleCoreIntegrationTestEagerSingletons.Helper
|
|
||||||
.injectEagerSingletons(component);
|
|
||||||
component.inject(this);
|
|
||||||
|
|
||||||
LocationUtils locationUtils = () -> "US";
|
|
||||||
SocketFactory torSocketFactory = SocketFactory.getDefault();
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
CircumventionProvider bridgeProvider = new CircumventionProvider() {
|
|
||||||
@Override
|
|
||||||
public boolean isTorProbablyBlocked(String countryCode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doBridgesWork(String countryCode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<BridgeType> getSuitableBridgeTypes(String countryCode) {
|
|
||||||
return singletonList(params.bridgeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getBridges(BridgeType bridgeType,
|
|
||||||
String countryCode, boolean letsEncrypt) {
|
|
||||||
return singletonList(params.bridge);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
|
||||||
networkManager, locationUtils, eventBus, torSocketFactory,
|
|
||||||
backoffFactory, resourceProvider, bridgeProvider,
|
|
||||||
batteryManager, clock, crypto, torDir, torSocksPort,
|
|
||||||
torControlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
deleteTestDirectory(torDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBridges() throws Exception {
|
|
||||||
if (params.stats.hasSucceeded(params.bridge)) {
|
|
||||||
LOG.info("Skipping previously successful bridge: " + params.bridge);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DuplexPlugin duplexPlugin =
|
|
||||||
factory.createPlugin(new TestPluginCallback());
|
|
||||||
assertNotNull(duplexPlugin);
|
|
||||||
UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
|
|
||||||
|
|
||||||
LOG.warning("Testing " + params.bridge);
|
|
||||||
try {
|
|
||||||
plugin.start();
|
|
||||||
long start = clock.currentTimeMillis();
|
|
||||||
long timeout = params.bridgeType == MEEK ? MEEK_TIMEOUT : TIMEOUT;
|
|
||||||
while (clock.currentTimeMillis() - start < timeout) {
|
|
||||||
if (plugin.getState() == ACTIVE) break;
|
|
||||||
clock.sleep(500);
|
|
||||||
}
|
|
||||||
if (plugin.getState() == ACTIVE) {
|
|
||||||
LOG.info("Connected to Tor: " + params.bridge);
|
|
||||||
params.stats.countSuccess(params.bridge);
|
|
||||||
} else {
|
|
||||||
LOG.warning("Could not connect to Tor within timeout: "
|
|
||||||
+ params.bridge);
|
|
||||||
params.stats.countFailure(params.bridge, params.essential);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
plugin.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Params {
|
|
||||||
|
|
||||||
private final String bridge;
|
|
||||||
private final BridgeType bridgeType;
|
|
||||||
private final Stats stats;
|
|
||||||
private final boolean essential;
|
|
||||||
|
|
||||||
private Params(String bridge, BridgeType bridgeType,
|
|
||||||
Stats stats, boolean essential) {
|
|
||||||
this.bridge = bridge;
|
|
||||||
this.bridgeType = bridgeType;
|
|
||||||
this.stats = stats;
|
|
||||||
this.essential = essential;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Stats {
|
|
||||||
|
|
||||||
@GuardedBy("this")
|
|
||||||
private final Set<String> successes = new HashSet<>();
|
|
||||||
@GuardedBy("this")
|
|
||||||
private final Multiset<String> failures = new Multiset<>();
|
|
||||||
@GuardedBy("this")
|
|
||||||
private final Set<String> unreachable = new TreeSet<>();
|
|
||||||
|
|
||||||
private synchronized boolean hasSucceeded(String bridge) {
|
|
||||||
return successes.contains(bridge);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void countSuccess(String bridge) {
|
|
||||||
successes.add(bridge);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void countFailure(String bridge,
|
|
||||||
boolean essential) {
|
|
||||||
if (failures.add(bridge) == ATTEMPTS_PER_BRIDGE) {
|
|
||||||
LOG.warning("Bridge is unreachable after "
|
|
||||||
+ ATTEMPTS_PER_BRIDGE + " attempts: " + bridge);
|
|
||||||
unreachable.add(bridge);
|
|
||||||
if (unreachable.size() > UNREACHABLE_BRIDGES_ALLOWED) {
|
|
||||||
fail(unreachable.size() + " bridges are unreachable: "
|
|
||||||
+ unreachable);
|
|
||||||
}
|
|
||||||
if (essential) {
|
|
||||||
fail("essential bridge is unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.tor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class TestPluginCallback implements PluginCallback {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Settings getSettings() {
|
|
||||||
return new Settings();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportProperties getLocalProperties() {
|
|
||||||
return new TransportProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<TransportProperties> getRemoteProperties() {
|
|
||||||
return emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mergeSettings(Settings s) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mergeLocalProperties(TransportProperties p) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pluginStateChanged(State state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleConnection(DuplexTransportConnection c) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleReader(TransportConnectionReader r) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleWriter(TransportConnectionWriter w) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.briarproject.bramble.test;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
|
||||||
import org.briarproject.bramble.BrambleJavaModule;
|
|
||||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
|
||||||
import org.briarproject.bramble.plugin.tor.BridgeTest;
|
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Component;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = {
|
|
||||||
BrambleCoreIntegrationTestModule.class,
|
|
||||||
BrambleCoreModule.class,
|
|
||||||
BrambleJavaModule.class,
|
|
||||||
ModularMailboxModule.class,
|
|
||||||
TestTorPortsModule.class,
|
|
||||||
TestPluginConfigModule.class,
|
|
||||||
})
|
|
||||||
public interface BrambleJavaIntegrationTestComponent
|
|
||||||
extends BrambleCoreIntegrationTestEagerSingletons {
|
|
||||||
|
|
||||||
void inject(BridgeTest init);
|
|
||||||
|
|
||||||
CircumventionProvider getCircumventionProvider();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package org.briarproject.bramble.test;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
class TestTorPortsModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@TorSocksPort
|
|
||||||
int provideTorSocksPort() {
|
|
||||||
return DEFAULT_SOCKS_PORT + 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@TorControlPort
|
|
||||||
int provideTorControlPort() {
|
|
||||||
return DEFAULT_CONTROL_PORT + 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,8 +15,6 @@ dependencyVerification {
|
|||||||
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
||||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82',
|
|
||||||
'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
|
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
@@ -26,12 +24,10 @@ dependencyVerification {
|
|||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:obfs4proxy-linux:0.0.14-tor1:obfs4proxy-linux-0.0.14-tor1.jar:9783b9c7ec588a5246f534a9c5782783c8c9821825f81c3e0c6f1ecee61cfcbb',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
|
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||||
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
|
'org.briarproject:onionwrapper-java:0.0.1:onionwrapper-java-0.0.1.jar:102ccea934d02b13702fd28e890e27e342db8b669a4c84bb54a3783cb8926552',
|
||||||
'org.briarproject:tor-linux:0.4.7.13:tor-linux-0.4.7.13.jar:9819ee973cbcdc133f7d04aef9d4b957a35087627a790e532142d15412a9636f',
|
|
||||||
'org.briarproject:tor-windows:0.4.7.13:tor-windows-0.4.7.13.jar:853d2769665614e26703cbe02e43b218b064c04a0bcd120fdc459cda45bd2606',
|
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 10420
|
versionCode 10500
|
||||||
versionName "1.4.20"
|
versionName "1.5.0"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||||
|
|
||||||
@@ -116,7 +116,6 @@ dependencies {
|
|||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
||||||
|
|
||||||
implementation 'org.briarproject:dont-kill-me-lib:0.2.5'
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||||
implementation "org.jsoup:jsoup:$jsoup_version"
|
implementation "org.jsoup:jsoup:$jsoup_version"
|
||||||
implementation 'info.guardianproject.panic:panic:1.0'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.BrambleAndroidEagerSingletons;
|
import org.briarproject.bramble.BrambleAndroidEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleAndroidModule;
|
import org.briarproject.bramble.BrambleAndroidModule;
|
||||||
import org.briarproject.bramble.BrambleAppComponent;
|
import org.briarproject.bramble.BrambleAppComponent;
|
||||||
@@ -25,12 +26,10 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
|||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||||
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
|
||||||
import org.briarproject.bramble.system.ClockModule;
|
import org.briarproject.bramble.system.ClockModule;
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
import org.briarproject.briar.BriarCoreModule;
|
||||||
@@ -84,6 +83,7 @@ import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
|||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
import org.briarproject.briar.api.test.TestDataCreator;
|
import org.briarproject.briar.api.test.TestDataCreator;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
|||||||
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.briarproject.briar.BuildConfig;
|
|
||||||
import org.briarproject.briar.android.account.DozeHelperModule;
|
import org.briarproject.briar.android.account.DozeHelperModule;
|
||||||
import org.briarproject.briar.android.account.LockManagerImpl;
|
import org.briarproject.briar.android.account.LockManagerImpl;
|
||||||
import org.briarproject.briar.android.account.SetupModule;
|
import org.briarproject.briar.android.account.SetupModule;
|
||||||
@@ -212,7 +211,7 @@ public class AppModule {
|
|||||||
@Override
|
@Override
|
||||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||||
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
||||||
if (featureFlags.shouldEnableMailbox()) simplex.add(mailbox);
|
simplex.add(mailbox);
|
||||||
if (SDK_INT >= 19) simplex.add(drive);
|
if (SDK_INT >= 19) simplex.add(drive);
|
||||||
return simplex;
|
return simplex;
|
||||||
}
|
}
|
||||||
@@ -353,11 +352,6 @@ public class AppModule {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableMailbox() {
|
|
||||||
return BuildConfig.DEBUG;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldEnablePrivateGroupsInCore() {
|
public boolean shouldEnablePrivateGroupsInCore() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import android.os.IBinder;
|
|||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.account.AccountManager;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ public class SetPasswordFragment extends SetupFragment {
|
|||||||
|
|
||||||
if (!viewModel.needToShowDozeFragment()) {
|
if (!viewModel.needToShowDozeFragment()) {
|
||||||
nextButton.setText(R.string.create_account_button);
|
nextButton.setText(R.string.create_account_button);
|
||||||
passwordConfirmation.setImeOptions(IME_ACTION_DONE);
|
int options = passwordConfirmation.getImeOptions();
|
||||||
|
passwordConfirmation.setImeOptions(options | IME_ACTION_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.getIsCreatingAccount()
|
viewModel.getIsCreatingAccount()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.transition.Transition;
|
|||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.system.Wakeful;
|
import org.briarproject.bramble.api.system.Wakeful;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
|||||||
@@ -5,16 +5,25 @@ import android.os.Bundle;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.FileImportError;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.FileImportSuccess;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.UrlImportError;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.UrlImportSuccess;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
|
import org.briarproject.briar.android.fragment.ErrorFragment;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class RssFeedActivity extends BriarActivity
|
public class RssFeedActivity extends BriarActivity
|
||||||
@@ -45,21 +54,29 @@ public class RssFeedActivity extends BriarActivity
|
|||||||
viewModel.getImportResult().observeEvent(this, this::onImportResult);
|
viewModel.getImportResult().observeEvent(this, this::onImportResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onImportResult(boolean result) {
|
private void onImportResult(@Nullable RssImportResult result) {
|
||||||
if (result) {
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
if (result instanceof UrlImportSuccess) {
|
||||||
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
|
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (result instanceof UrlImportError) {
|
||||||
String url = viewModel.getUrlFailedImport();
|
String url = ((UrlImportError) result).url;
|
||||||
if (url == null) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
RssFeedImportFailedDialogFragment dialog =
|
RssFeedImportFailedDialogFragment dialog =
|
||||||
RssFeedImportFailedDialogFragment.newInstance(url);
|
RssFeedImportFailedDialogFragment.newInstance(url);
|
||||||
dialog.show(getSupportFragmentManager(),
|
dialog.show(fm, RssFeedImportFailedDialogFragment.TAG);
|
||||||
RssFeedImportFailedDialogFragment.TAG);
|
} else if (result instanceof FileImportSuccess) {
|
||||||
|
// pop stack back to before the initial import fragment
|
||||||
|
fm.popBackStackImmediate(RssFeedImportFragment.TAG,
|
||||||
|
POP_BACK_STACK_INCLUSIVE);
|
||||||
|
} else if (result instanceof FileImportError) {
|
||||||
|
// pop stack back to initial import fragment
|
||||||
|
fm.popBackStackImmediate(RssFeedImportFragment.TAG, 0);
|
||||||
|
// show error fragment
|
||||||
|
Fragment f = ErrorFragment.newInstance(
|
||||||
|
getString(R.string.blogs_rss_feeds_import_error));
|
||||||
|
String tag = ErrorFragment.TAG;
|
||||||
|
showFragment(fm, f, tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -13,18 +17,27 @@ import android.widget.ProgressBar;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
import org.briarproject.briar.android.fragment.ProgressFragment;
|
||||||
|
import org.briarproject.briar.android.util.ActivityLaunchers.GetContentAdvanced;
|
||||||
|
import org.briarproject.briar.android.util.ActivityLaunchers.OpenDocumentAdvanced;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
|
import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -39,6 +52,15 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
private Button importButton;
|
private Button importButton;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
|
@RequiresApi(19)
|
||||||
|
private final ActivityResultLauncher<String[]> docLauncher =
|
||||||
|
registerForActivityResult(new OpenDocumentAdvanced(),
|
||||||
|
this::onFileChosen);
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String> contentLauncher =
|
||||||
|
registerForActivityResult(new GetContentAdvanced(),
|
||||||
|
this::onFileChosen);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
@@ -52,6 +74,7 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
||||||
|
if (SDK_INT >= 19) setHasOptionsMenu(true);
|
||||||
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
@@ -92,11 +115,40 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
if (SDK_INT >= 19) {
|
||||||
|
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
||||||
|
}
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_import_file && SDK_INT >= 19) {
|
||||||
|
launchActivityToOpenFile(requireContext(), docLauncher,
|
||||||
|
contentLauncher, "*/*");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUniqueTag() {
|
public String getUniqueTag() {
|
||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onFileChosen(@Nullable Uri uri) {
|
||||||
|
if (uri == null) return;
|
||||||
|
// show progress fragment
|
||||||
|
Fragment f = ProgressFragment.newInstance(
|
||||||
|
getString(R.string.blogs_rss_feeds_import_progress));
|
||||||
|
String tag = ProgressFragment.TAG;
|
||||||
|
showFragment(getParentFragmentManager(), f, tag);
|
||||||
|
// view model will import and change state that activity will react to
|
||||||
|
viewModel.importFeed(uri);
|
||||||
|
}
|
||||||
|
|
||||||
private void enableOrDisableImportButton() {
|
private void enableOrDisableImportButton() {
|
||||||
String url = urlInput.getText().toString();
|
String url = urlInput.getText().toString();
|
||||||
importButton.setEnabled(viewModel.validateAndNormaliseUrl(url) != null);
|
importButton.setEnabled(viewModel.validateAndNormaliseUrl(url) != null);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.net.Uri;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
@@ -11,6 +13,10 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.FileImportError;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.FileImportSuccess;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.UrlImportError;
|
||||||
|
import org.briarproject.briar.android.blog.RssImportResult.UrlImportSuccess;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||||
@@ -20,6 +26,7 @@ import org.briarproject.briar.api.feed.FeedManager;
|
|||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,6 +37,7 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
@@ -52,11 +60,9 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
private final MutableLiveData<LiveResult<List<Feed>>> feeds =
|
private final MutableLiveData<LiveResult<List<Feed>>> feeds =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private volatile String urlFailedImport = null;
|
|
||||||
private final MutableLiveData<Boolean> isImporting =
|
private final MutableLiveData<Boolean> isImporting =
|
||||||
new MutableLiveData<>(false);
|
new MutableLiveData<>(false);
|
||||||
private final MutableLiveEvent<Boolean> importResult =
|
private final MutableLiveEvent<RssImportResult> importResult =
|
||||||
new MutableLiveEvent<>();
|
new MutableLiveEvent<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -120,7 +126,7 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEvent<Boolean> getImportResult() {
|
LiveEvent<RssImportResult> getImportResult() {
|
||||||
return importResult;
|
return importResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +136,6 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
|
|
||||||
void importFeed(String url) {
|
void importFeed(String url) {
|
||||||
isImporting.setValue(true);
|
isImporting.setValue(true);
|
||||||
urlFailedImport = null;
|
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
Feed feed = feedManager.addFeed(url);
|
Feed feed = feedManager.addFeed(url);
|
||||||
@@ -145,19 +150,38 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
updated = feedList;
|
updated = feedList;
|
||||||
}
|
}
|
||||||
feeds.postValue(new LiveResult<>(updated));
|
feeds.postValue(new LiveResult<>(updated));
|
||||||
importResult.postEvent(true);
|
importResult.postEvent(new UrlImportSuccess());
|
||||||
} catch (DbException | IOException e) {
|
} catch (DbException | IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
urlFailedImport = url;
|
importResult.postEvent(new UrlImportError(url));
|
||||||
importResult.postEvent(false);
|
|
||||||
} finally {
|
} finally {
|
||||||
isImporting.postValue(false);
|
isImporting.postValue(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@UiThread
|
||||||
String getUrlFailedImport() {
|
void importFeed(Uri uri) {
|
||||||
return urlFailedImport;
|
ContentResolver contentResolver = getApplication().getContentResolver();
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
try (InputStream is = contentResolver.openInputStream(uri)) {
|
||||||
|
Feed feed = feedManager.addFeed(is);
|
||||||
|
// Update the feed if it was already present
|
||||||
|
List<Feed> feedList = getList(feeds);
|
||||||
|
if (feedList == null) feedList = new ArrayList<>();
|
||||||
|
List<Feed> updated = updateListItems(feedList,
|
||||||
|
f -> f.equals(feed), f -> feed);
|
||||||
|
// Add the feed if it wasn't already present
|
||||||
|
if (updated == null) {
|
||||||
|
feedList.add(feed);
|
||||||
|
updated = feedList;
|
||||||
|
}
|
||||||
|
feeds.postValue(new LiveResult<>(updated));
|
||||||
|
importResult.postEvent(new FileImportSuccess());
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
importResult.postEvent(new FileImportError());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
abstract class RssImportResult {
|
||||||
|
|
||||||
|
static class UrlImportSuccess extends RssImportResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
static class UrlImportError extends RssImportResult {
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
UrlImportError(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileImportSuccess extends RssImportResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileImportError extends RssImportResult {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -171,7 +171,7 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
private void handleExistingContact(String name, Author existing) {
|
private void handleExistingContact(String name, Author existing) {
|
||||||
OnClickListener listener = (d, w) -> {
|
OnClickListener listener = (d, w) -> {
|
||||||
d.dismiss();
|
d.dismiss();
|
||||||
String str = getString(R.string.contact_already_exists, name);
|
String str = getString(R.string.contact_already_exists_general);
|
||||||
Toast.makeText(getContext(), str, LENGTH_LONG).show();
|
Toast.makeText(getContext(), str, LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import android.app.Activity;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.account.AccountManager;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.briarproject.briar.android.BriarService;
|
import org.briarproject.briar.android.BriarService;
|
||||||
import org.briarproject.briar.android.BriarService.BriarServiceConnection;
|
import org.briarproject.briar.android.BriarService.BriarServiceConnection;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user