mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
187 Commits
beta-1.4.1
...
alpha-1.4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
0b94814620 | ||
|
|
e82e11acfa | ||
|
|
795461d9a8 | ||
|
|
7b8d01cfe0 | ||
|
|
abd04ee7f5 | ||
|
|
cc5365eaf0 | ||
|
|
6b20b03698 | ||
|
|
9da7fbf4f6 | ||
|
|
f64f442fcf | ||
|
|
6eda2f6d13 | ||
|
|
6faa095dfb | ||
|
|
4007fca668 | ||
|
|
28a747f7f3 | ||
|
|
fd2d5c9173 | ||
|
|
8f7bb9d26b | ||
|
|
dc220200b6 | ||
|
|
0cea137d75 | ||
|
|
2eef34f424 | ||
|
|
a68fff9dd2 | ||
|
|
ddc8f4a7d7 | ||
|
|
f961b6a80b | ||
|
|
93439d9c17 | ||
|
|
f3ee884816 | ||
|
|
8ca22043cf | ||
|
|
9353b78da8 | ||
|
|
429bbe1275 | ||
|
|
c5fb1416bd | ||
|
|
e52cbd896e | ||
|
|
ab1b8784b7 | ||
|
|
55a4daa92f | ||
|
|
e52250f1e4 | ||
|
|
33d01aac8c | ||
|
|
b920382e44 | ||
|
|
1a2f85f701 | ||
|
|
186bcc0b47 | ||
|
|
8b9140f477 | ||
|
|
f959c32935 | ||
|
|
1c060bc6db | ||
|
|
5e44e4d308 | ||
|
|
75d5dec45f | ||
|
|
d825227eb5 | ||
|
|
967dd1f18d | ||
|
|
4a4147b563 | ||
|
|
08b72af647 | ||
|
|
528e090c6f | ||
|
|
652f9e5705 | ||
|
|
6a91ec7a6b | ||
|
|
c3a9eff96b | ||
|
|
bd05d893eb | ||
|
|
6965bc0acd | ||
|
|
c6e9554026 | ||
|
|
ab8734e373 | ||
|
|
267956b36c | ||
|
|
ec84ddb38b | ||
|
|
ba2db48d8e | ||
|
|
186f61f771 | ||
|
|
47971517cd | ||
|
|
8db182d7e5 | ||
|
|
d44a609d0c | ||
|
|
0a1892d39f | ||
|
|
9b092da37a | ||
|
|
7a3ffcbae6 | ||
|
|
852e2c29e3 | ||
|
|
1b087d59d4 | ||
|
|
30ce8651b5 | ||
|
|
80a8ee4de9 | ||
|
|
354f3bc1cf | ||
|
|
1e6b018ff4 | ||
|
|
eba489bb98 | ||
|
|
2bfdcaaa42 | ||
|
|
c2e71ef52f | ||
|
|
9ee8fe74ba | ||
|
|
95d8783852 | ||
|
|
b4f3604584 | ||
|
|
badccac90c | ||
|
|
1b8d1a5a8d | ||
|
|
2fe57d2597 | ||
|
|
904d5b2ce2 | ||
|
|
1911b3dd97 | ||
|
|
bd430a1009 | ||
|
|
c16d0e8f45 | ||
|
|
847273c558 | ||
|
|
b9bac8b6a5 | ||
|
|
c855967d56 | ||
|
|
bae97e3312 | ||
|
|
f1be3031a7 | ||
|
|
3173486b3b | ||
|
|
c445b21d58 | ||
|
|
9a16cf6e54 | ||
|
|
a22990c1ed | ||
|
|
8262fa183b | ||
|
|
c3f3c6211d | ||
|
|
0c40f39a90 | ||
|
|
20b9aa86f7 | ||
|
|
a00c920382 | ||
|
|
e40d6552bc | ||
|
|
5e1564de6c | ||
|
|
155daae8d0 | ||
|
|
6680abf925 | ||
|
|
9bbfea525a | ||
|
|
979ef077b3 | ||
|
|
b7e1a987fc | ||
|
|
a705caa5fa | ||
|
|
441b28fede | ||
|
|
9c95534f39 | ||
|
|
57015c36b2 | ||
|
|
a2815c75a3 | ||
|
|
9ce82af856 | ||
|
|
4b792ff040 | ||
|
|
904355b0a6 | ||
|
|
02f2fdd4a1 | ||
|
|
9f039ff0b8 | ||
|
|
ba83290fcd | ||
|
|
05f84000b3 | ||
|
|
7302bf9d7a | ||
|
|
8c269541c3 | ||
|
|
324ca1b50b | ||
|
|
79730484c0 | ||
|
|
86fb648dae | ||
|
|
52809d8f2d | ||
|
|
0a906998fe | ||
|
|
4a65bc1726 | ||
|
|
f395ab1cb5 | ||
|
|
e6c051fee4 | ||
|
|
e76701f988 | ||
|
|
e6616a8c36 | ||
|
|
824a9e1124 | ||
|
|
113793045f | ||
|
|
c04937b1fa | ||
|
|
e8994d503e | ||
|
|
768bb6fc64 | ||
|
|
645eba7fe7 | ||
|
|
c76ed41958 | ||
|
|
4a2c1113c1 | ||
|
|
b6f78a8667 | ||
|
|
53c7c81c0f | ||
|
|
6dd250a1ed | ||
|
|
2e2c720241 | ||
|
|
7e3cf8f162 | ||
|
|
dab8d731fa | ||
|
|
65509137b6 | ||
|
|
48ab5f4966 | ||
|
|
a37447d3e8 | ||
|
|
5d1d0fb12a | ||
|
|
790818623f | ||
|
|
d46a227cfc | ||
|
|
e986d4b214 | ||
|
|
7bcffdf0d1 | ||
|
|
f4dd3c4f06 | ||
|
|
f42cf00c35 | ||
|
|
38c347552b | ||
|
|
bab6ec70f5 | ||
|
|
8db25738e2 | ||
|
|
28f770df89 | ||
|
|
a720501fde | ||
|
|
648911b3ed | ||
|
|
ab6c925a9c |
@@ -83,7 +83,8 @@ android test:
|
||||
test_reproducible:
|
||||
stage: check_reproducibility
|
||||
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[RELEASE_TAG]=\"briar ${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[RELEASE_TAG]=\"briar-headless ${CI_COMMIT_REF_NAME}\" https://code.briarproject.org/api/v4/projects/307/trigger/pipeline"
|
||||
only:
|
||||
- tags
|
||||
|
||||
@@ -107,6 +108,10 @@ bridge test:
|
||||
mailbox integration test:
|
||||
extends: .optional_tests
|
||||
rules:
|
||||
- changes:
|
||||
- mailbox-integration-tests/**/*
|
||||
when: on_success
|
||||
allow_failure: false
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_TAG == null'
|
||||
|
||||
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@@ -35,9 +35,6 @@
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import com.android.build.gradle.tasks.MergeResources
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
compileSdkVersion 33
|
||||
buildToolsVersion '33.0.0'
|
||||
|
||||
packagingOptions {
|
||||
doNotStrip '**/*.so'
|
||||
@@ -14,12 +12,13 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10415
|
||||
versionName "1.4.15"
|
||||
targetSdkVersion 31
|
||||
versionCode 10422
|
||||
versionName "1.4.22"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunnerArguments disableAnalytics: 'true'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -41,8 +40,16 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
// 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,
|
||||
// even though the bramble-api test classes are provided by the testImplementation dependency
|
||||
// below and the compiler can find them
|
||||
implementation project(':bramble-api')
|
||||
|
||||
implementation project(':bramble-core')
|
||||
|
||||
implementation 'androidx.annotation:annotation:1.5.0'
|
||||
|
||||
tor "org.briarproject:tor-android:$tor_version"
|
||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||
tor "org.briarproject:snowflake-android:$snowflake_version"
|
||||
@@ -52,18 +59,18 @@ dependencies {
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
}
|
||||
|
||||
def torBinariesDir = 'src/main/res/raw'
|
||||
def torLibsDir = 'src/main/jniLibs'
|
||||
|
||||
task cleanTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
||||
delete fileTree(torLibsDir) { include '**/*.so' }
|
||||
}
|
||||
}
|
||||
@@ -71,6 +78,7 @@ task cleanTorBinaries {
|
||||
clean.dependsOn cleanTorBinaries
|
||||
|
||||
task unpackTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
configurations.tor.each { outer ->
|
||||
zipTree(outer).each { inner ->
|
||||
@@ -105,8 +113,4 @@ task unpackTorBinaries {
|
||||
dependsOn cleanTorBinaries
|
||||
}
|
||||
|
||||
tasks.withType(MergeResources) {
|
||||
inputs.dir torBinariesDir
|
||||
inputs.dir torLibsDir
|
||||
dependsOn unpackTorBinaries
|
||||
}
|
||||
preBuild.dependsOn unpackTorBinaries
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
<manifest
|
||||
package="org.briarproject.bramble"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.briarproject.bramble">
|
||||
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_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 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="31" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation"
|
||||
tools:targetApi="31" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.socks.SocksModule;
|
||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
@@ -18,6 +19,7 @@ import dagger.Module;
|
||||
AndroidSystemModule.class,
|
||||
AndroidTaskSchedulerModule.class,
|
||||
AndroidWakefulIoExecutorModule.class,
|
||||
DefaultThreadFactoryModule.class,
|
||||
CircumventionModule.class,
|
||||
DnsModule.class,
|
||||
ReportingModule.class,
|
||||
|
||||
@@ -118,6 +118,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
try {
|
||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||
boolean connected = net != null && net.isConnected();
|
||||
// Research into Android's behavior to check network connectivity
|
||||
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19)
|
||||
// has shown that NetworkInfo#isConnected() returns true if the device
|
||||
// is connected to any Wifi, independent of whether any specific IP
|
||||
// address can be reached using it or any domain names can be resolved.
|
||||
boolean wifi = false, ipv6Only = false;
|
||||
if (connected) {
|
||||
wifi = net.getType() == TYPE_WIFI;
|
||||
|
||||
@@ -55,6 +55,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
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.bramble.util.AndroidUtils.hasBtConnectPermission;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -97,6 +98,11 @@ class AndroidBluetoothPlugin extends
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBluetoothAccessible() {
|
||||
return hasBtConnectPermission(app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws PluginException {
|
||||
super.start();
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.StrictMode;
|
||||
import android.provider.Settings;
|
||||
@@ -15,11 +14,17 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.os.Build.FINGERPRINT;
|
||||
import static android.os.Build.SERIAL;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myTid;
|
||||
import static android.os.Process.myUid;
|
||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||
|
||||
@Immutable
|
||||
@@ -39,22 +44,27 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
@Override
|
||||
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||
super.writeToEntropyPool(out);
|
||||
out.writeInt(android.os.Process.myPid());
|
||||
out.writeInt(android.os.Process.myTid());
|
||||
out.writeInt(android.os.Process.myUid());
|
||||
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
||||
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
||||
out.writeInt(myPid());
|
||||
out.writeInt(myTid());
|
||||
out.writeInt(myUid());
|
||||
if (FINGERPRINT != null) out.writeUTF(FINGERPRINT);
|
||||
if (SERIAL != null) out.writeUTF(SERIAL);
|
||||
ContentResolver contentResolver = appContext.getContentResolver();
|
||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||
if (id != null) out.writeUTF(id);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt != null) {
|
||||
for (BluetoothDevice device : bt.getBondedDevices())
|
||||
parcel.writeParcelable(device, 0);
|
||||
// On API 31 and higher we need permission to access bonded devices
|
||||
if (SDK_INT < 31) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt != null) {
|
||||
@SuppressLint("MissingPermission")
|
||||
Set<BluetoothDevice> deviceSet = bt.getBondedDevices();
|
||||
for (BluetoothDevice device : deviceSet)
|
||||
parcel.writeParcelable(device, 0);
|
||||
}
|
||||
out.write(parcel.marshall());
|
||||
parcel.recycle();
|
||||
}
|
||||
out.write(parcel.marshall());
|
||||
parcel.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,7 +87,7 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
.invoke(null, (Object) seed);
|
||||
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||
int bytesRead = (Integer) Class.forName(
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_load_file", String.class, long.class)
|
||||
.invoke(null, "/dev/urandom", 1024);
|
||||
if (bytesRead != 1024) throw new IOException();
|
||||
|
||||
@@ -41,6 +41,7 @@ import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID;
|
||||
import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
@@ -199,7 +200,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
||||
Intent i = new Intent(app, AlarmReceiver.class);
|
||||
i.putExtra(EXTRA_PID, android.os.Process.myPid());
|
||||
return PendingIntent.getBroadcast(app, REQUEST_ALARM, i,
|
||||
FLAG_CANCEL_CURRENT);
|
||||
getImmutableFlags(FLAG_CANCEL_CURRENT));
|
||||
}
|
||||
|
||||
private class ScheduledTask
|
||||
|
||||
@@ -22,8 +22,13 @@ import java.util.Scanner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.Manifest.permission.BLUETOOTH_CONNECT;
|
||||
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myUid;
|
||||
import static java.lang.Runtime.getRuntime;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
@@ -48,6 +53,11 @@ public class AndroidUtils {
|
||||
return abis;
|
||||
}
|
||||
|
||||
public static boolean hasBtConnectPermission(Context ctx) {
|
||||
return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(),
|
||||
myUid()) == PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
public static String getBluetoothAddress(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
return getBluetoothAddressAndMethod(ctx, adapter).getFirst();
|
||||
@@ -55,6 +65,9 @@ public class AndroidUtils {
|
||||
|
||||
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
// If we don't have permission to access the adapter's address, let
|
||||
// the caller know we can't find it
|
||||
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
|
||||
// Return the adapter's address if it's valid and not fake
|
||||
@SuppressLint("HardwareIds")
|
||||
String address = adapter.getAddress();
|
||||
@@ -139,4 +152,11 @@ public class AndroidUtils {
|
||||
public static boolean isUiThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
|
||||
public static int getImmutableFlags(int flags) {
|
||||
if (SDK_INT >= 23) {
|
||||
return FLAG_IMMUTABLE | flags;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,164 +2,58 @@ dependencyVerification {
|
||||
verify = [
|
||||
'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.android.tools.analytics-library:protos:30.0.3:protos-30.0.3.jar:f62b89dcd9de719c6a7b7e15fb1dd20e45b57222e675cf633607bd0ed6bca7e7',
|
||||
'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15',
|
||||
'com.android.tools.analytics-library:tracker:30.0.3:tracker-30.0.3.jar:5d0ef35bf6733e96210b5085a2a202152921bf834d345959dce1ca3369b528df',
|
||||
'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca',
|
||||
'com.android.tools.build:apksig:7.0.3:apksig-7.0.3.jar:012337a2803c9a30dfc41dcbc6450686ee9e5f582549f7f126479f743a343ec9',
|
||||
'com.android.tools.build:apkzlib:7.0.3:apkzlib-7.0.3.jar:b31e53174c92db83c5cc6e7dac6734ea4e907a72e452c2bf1818dfd082c59397',
|
||||
'com.android.tools.build:builder-model:7.0.3:builder-model-7.0.3.jar:483f99d7494a5bed027e1e8d29111384cf535d4842f0be5a79805bd44bb68d4e',
|
||||
'com.android.tools.build:builder-test-api:7.0.3:builder-test-api-7.0.3.jar:f6de4bc2cef545e8367bf82d7c733829c7be3b0b3b8b09fd8c58f2150e59ab46',
|
||||
'com.android.tools.build:builder:7.0.3:builder-7.0.3.jar:c6952da0094b094c2ba0fe84c675622097c5d9b9f9beb53485b860320540cf1d',
|
||||
'com.android.tools.build:manifest-merger:30.0.3:manifest-merger-30.0.3.jar:72b346ba6318b4b6260e6e49df4bea5da2e12329ab6c2beb2269c49a9f51f178',
|
||||
'com.android.tools.ddms:ddmlib:30.0.3:ddmlib-30.0.3.jar:7a914a68ab93393657297234e2f37b22410ae9a433cba692ce8c727c9607e3bb',
|
||||
'com.android.tools.external.com-intellij:intellij-core:30.0.3:intellij-core-30.0.3.jar:1ebe858d3f58eeaa8c06507f8ac0f1c7051e6c61f35a70f3c3967d5734d3abc5',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:30.0.3:kotlin-compiler-30.0.3.jar:ed00e441f427cb4e0d418287b9da30b12b7f735f9af32e6b5d3dc960b6a742fc',
|
||||
'com.android.tools.external.org-jetbrains:uast:30.0.3:uast-30.0.3.jar:a77801bee6ff509910e459525c9c34d7f04b066ade123547f16f1917548eadea',
|
||||
'com.android.tools.layoutlib:layoutlib-api:30.0.3:layoutlib-api-30.0.3.jar:4caa87e9ca2e11315f650d576cd59fec1793373bc3fca3f6d53c029e7534e7c4',
|
||||
'com.android.tools.lint:lint-api:30.0.3:lint-api-30.0.3.jar:bcecbd2f752a6560096a9029a47d1de6bd788a51bab505c5ebfba6a18524b983',
|
||||
'com.android.tools.lint:lint-checks:30.0.3:lint-checks-30.0.3.jar:25a7cd42dc3ad502337f131fb8b7e873c53301db0a67b1c64dd4ae7a8eb66cec',
|
||||
'com.android.tools.lint:lint-gradle:30.0.3:lint-gradle-30.0.3.jar:94544d6147a809bf2fd3440e51f28a4e42e547d74aab53eefd74938cdad42c26',
|
||||
'com.android.tools.lint:lint-model:30.0.3:lint-model-30.0.3.jar:0b940a7f575c2ff5cbd038260f41dde686a93c672213881ead3ce8af3513b396',
|
||||
'com.android.tools.lint:lint:30.0.3:lint-30.0.3.jar:ee4f11001e0c7e3b776e0d67399ad354b19b0f168822ec2b7db47c0910ed227d',
|
||||
'com.android.tools:annotations:30.0.3:annotations-30.0.3.jar:5c1944982fda8555855c4f5422fabf0dc8e2306e1f5460e9ad82dae71316bc31',
|
||||
'com.android.tools:common:30.0.3:common-30.0.3.jar:8751efaaf2c2ddd1f0a37526c794347def6a3057ca9fc510307c13a6cf0d036f',
|
||||
'com.android.tools:dvlib:30.0.3:dvlib-30.0.3.jar:5affafcec390041e5afd64cb924153f5e474db47ee8ccc2f555b495083141233',
|
||||
'com.android.tools:repository:30.0.3:repository-30.0.3.jar:0a40c6f16c506903ce2c609affd8228aceda73a69d93dfa42d4f02b8491449f6',
|
||||
'com.android.tools:sdk-common:30.0.3:sdk-common-30.0.3.jar:b45570a380360236ffee0f6bb593d66b673bad3834dfe0d6c9871fa7188ee0eb',
|
||||
'com.android.tools:sdklib:30.0.3:sdklib-30.0.3.jar:7088f20a414fab170a21e457825e14ebe099f753558e02c8acc12c67eb412162',
|
||||
'com.android:signflinger:7.0.3:signflinger-7.0.3.jar:903a4536db3e96b4e1e1dc1e400eb0b91bf7866d9b39cd7ec94d75dde158f152',
|
||||
'com.android:zipflinger:7.0.3:zipflinger-7.0.3.jar:fd209c960a3eff7a339e6fcba07d5e9ef4604d1633c69ab2df987460d9804140',
|
||||
'com.beust:jcommander:1.78:jcommander-1.78.jar:7891debb84b5f83e9bd57593ebece3399abbe0fd938cf306b3534c57913b9615',
|
||||
'com.github.javaparser:javaparser-core:3.17.0:javaparser-core-3.17.0.jar:23f5c982e1c7771423d37d52c774e8d2e80fd7ea7305ebe448797a96f67e6fca',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.code.gson:gson:2.8.6:gson-2.8.6.jar:c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f',
|
||||
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
|
||||
'com.google.dagger:dagger-producers:2.33:dagger-producers-2.33.jar:5897f0b6eef799c2adfe3ccacc58c0fb374d58acb063c3ebe5366c38a8bce5c8',
|
||||
'com.google.dagger:dagger-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
|
||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
||||
'com.google.errorprone:error_prone_annotations:2.3.4:error_prone_annotations-2.3.4.jar:baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c',
|
||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
||||
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
|
||||
'com.google.guava:guava:30.1-jre:guava-30.1-jre.jar:e6dd072f9d3fe02a4600688380bd422bdac184caf6fe2418cfdd0934f09432aa',
|
||||
'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.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||
'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
|
||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||
'com.sun.istack:istack-commons-runtime:3.0.8:istack-commons-runtime-3.0.8.jar:4ffabb06be454a05e4398e20c77fa2b6308d4b88dfbef7ca30a76b5b7d5505ef',
|
||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.16:FastInfoset-1.2.16.jar:056f3a1e144409f21ed16afc26805f58e9a21f3fce1543c42d400719d250c511',
|
||||
'com.thoughtworks.qdox:qdox:1.12.1:qdox-1.12.1.jar:21fba22f830e9268f07cf4ab2d99e8181abbdcb0cb91ee0228eb3cb918dcdd1d',
|
||||
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
|
||||
'commons-io:commons-io:2.4:commons-io-2.4.jar:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
|
||||
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
||||
'info.picocli:picocli:4.5.2:picocli-4.5.2.jar:b4395e9a67932616efd2245d984bf5fcd453c2c5049558c3ce959ac2af4d3fac',
|
||||
'it.unimi.dsi:fastutil:8.4.0:fastutil-8.4.0.jar:2ad2824a4a0a0eb836b52ee2fc84ba2134f44bce7bfa54015ae3f31c710a3071',
|
||||
'jakarta.activation:jakarta.activation-api:1.2.1:jakarta.activation-api-1.2.1.jar:8b0a0f52fa8b05c5431921a063ed866efaa41dadf2e3a7ee3e1961f2b0d9645b',
|
||||
'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2:jakarta.xml.bind-api-2.3.2.jar:69156304079bdeed9fc0ae3b39389f19b3cc4ba4443bc80508995394ead742ea',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'jline:jline:2.14.6:jline-2.14.6.jar:97d1acaac82409be42e622d7a54d3ae9d08517e8aefdea3d2ba9791150c2f02d',
|
||||
'junit:junit:4.13.1:junit-4.13.1.jar:c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
'net.java.dev.jna:jna-platform:5.6.0:jna-platform-5.6.0.jar:9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7',
|
||||
'net.java.dev.jna:jna:5.6.0:jna-5.6.0.jar:5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf',
|
||||
'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.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
||||
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.apache.ant:ant-antlr:1.10.9:ant-antlr-1.10.9.jar:7623dc9d0f20ea713290c6bf1a23f4c059447aef7ff9f5b2be75960f3f028d2e',
|
||||
'org.apache.ant:ant-junit:1.10.9:ant-junit-1.10.9.jar:960bdc8827954d62206ba42d0a68a7ee4476175ba47bb113e17e77cce7394630',
|
||||
'org.apache.ant:ant-launcher:1.10.9:ant-launcher-1.10.9.jar:fcce891f57f3be72149ff96ac2a80574165b3e0839866b95d24528f3027d50c1',
|
||||
'org.apache.ant:ant:1.10.9:ant-1.10.9.jar:0715478af585ea80a18985613ebecdc7922122d45b2c3c970ff9b352cddb75fc',
|
||||
'org.apache.commons:commons-compress:1.20:commons-compress-1.20.jar:0aeb625c948c697ea7b205156e112363b59ed5e2551212cd4e460bdb72c7c06e',
|
||||
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
|
||||
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
|
||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
|
||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
||||
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.checkerframework:checker-qual:3.5.0:checker-qual-3.5.0.jar:729990b3f18a95606fc2573836b6958bcdb44cb52bfbd1b7aa9c339cff35a5a4',
|
||||
'org.codehaus.groovy:groovy-ant:3.0.7:groovy-ant-3.0.7.jar:6ed2ba82813d128f7050c24142e87b3dc2ad8b504786280eb03e81f0cf6a5793',
|
||||
'org.codehaus.groovy:groovy-astbuilder:3.0.7:groovy-astbuilder-3.0.7.jar:b290451eb1583666e906c41f7d14747b4cc96363c99c478b244634fd5dfc9013',
|
||||
'org.codehaus.groovy:groovy-cli-picocli:3.0.7:groovy-cli-picocli-3.0.7.jar:71b4bd11fb30a9c7b5618e22122c9c5141958fb27f4dcf0068b6f715088f6916',
|
||||
'org.codehaus.groovy:groovy-console:3.0.7:groovy-console-3.0.7.jar:0541b358b6b8e5363215026736168fccfec1d91bac678d066fa77349eeeaa5dd',
|
||||
'org.codehaus.groovy:groovy-datetime:3.0.7:groovy-datetime-3.0.7.jar:b9823d14b1a4f94236ae2f8a471701aab17e093e1b33402b91550b5c8dd88f04',
|
||||
'org.codehaus.groovy:groovy-docgenerator:3.0.7:groovy-docgenerator-3.0.7.jar:bf53f7a11c9eb1e278e1b8ed2714c741bcf781235c803ad3ba1555f2614573f3',
|
||||
'org.codehaus.groovy:groovy-groovydoc:3.0.7:groovy-groovydoc-3.0.7.jar:86b24dfc23c005066ab83927cdb54177f06c9531773f2e2d2ecc9a131f7c2677',
|
||||
'org.codehaus.groovy:groovy-groovysh:3.0.7:groovy-groovysh-3.0.7.jar:5c40e78cbc09726aedd1c75fab112d245d665d6294870f9119e6cd3013ed14ab',
|
||||
'org.codehaus.groovy:groovy-jmx:3.0.7:groovy-jmx-3.0.7.jar:0a89f3007884eb156751937d93382038b83d39c7c2f0ab156ebf251a7251f2ab',
|
||||
'org.codehaus.groovy:groovy-json:3.0.7:groovy-json-3.0.7.jar:df1f0ee475e3fc93a6a0d17548294e160cca5de6d9d36817a7be1fbe650de03b',
|
||||
'org.codehaus.groovy:groovy-jsr223:3.0.7:groovy-jsr223-3.0.7.jar:1dbd969595332416193baa660fbb45743d19696eaa25fe98e591a2739e13517e',
|
||||
'org.codehaus.groovy:groovy-macro:3.0.7:groovy-macro-3.0.7.jar:c6cc06df526b39e2c359e2435f0071594c5a1c7babafaa6c184fdd8fa931531f',
|
||||
'org.codehaus.groovy:groovy-nio:3.0.7:groovy-nio-3.0.7.jar:db54c577882b294cd8c975ec5451596441baf54781319c61627dca0e0c2361ef',
|
||||
'org.codehaus.groovy:groovy-servlet:3.0.7:groovy-servlet-3.0.7.jar:5b6a909bf501c209adfb6205b9e740649609074455fd979bf9da4853e6ff9a39',
|
||||
'org.codehaus.groovy:groovy-sql:3.0.7:groovy-sql-3.0.7.jar:252bb6c74e1a9f41756ad4fbd3b0d2eddc93bb61109961dd1952a37bf2d57a64',
|
||||
'org.codehaus.groovy:groovy-swing:3.0.7:groovy-swing-3.0.7.jar:bd942032d9328d54c6679c49a41f6caa0d4a0039ebe598493b8a647730d98cff',
|
||||
'org.codehaus.groovy:groovy-templates:3.0.7:groovy-templates-3.0.7.jar:f119e07f650ef186ae5a4b944f9e30915b14311bad47c94a6b32de8d4f69bc80',
|
||||
'org.codehaus.groovy:groovy-test-junit5:3.0.7:groovy-test-junit5-3.0.7.jar:c16eeea07b8e396891e266d7ba9388b24ac804237ffdd9a792b0d08969bad014',
|
||||
'org.codehaus.groovy:groovy-test:3.0.7:groovy-test-3.0.7.jar:f71afd7c25d43017f89ea47e6de6daec971d159047dae083c1513a8422d44b90',
|
||||
'org.codehaus.groovy:groovy-testng:3.0.7:groovy-testng-3.0.7.jar:713d5f2231bbb5712aefd362151b9ffd884aeb7ef2e773315cc54259cbdd063d',
|
||||
'org.codehaus.groovy:groovy-xml:3.0.7:groovy-xml-3.0.7.jar:8a62e7c9ddece3e82676c4bef2f2c100f459602cd1fb6a14e94187bf863e97ff',
|
||||
'org.codehaus.groovy:groovy:3.0.7:groovy-3.0.7.jar:51d1777e8dd1f00e60ea56e00d8a354ff5aab1f00fc8464ae8d39d71867e401f',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
'org.glassfish.jaxb:jaxb-runtime:2.3.2:jaxb-runtime-2.3.2.jar:e6e0a1e89fb6ff786279e6a0082d5cef52dc2ebe67053d041800737652b4fd1b',
|
||||
'org.glassfish.jaxb:txw2:2.3.2:txw2-2.3.2.jar:4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.briarproject:tor-android:0.4.7.13:tor-android-0.4.7.13.jar:7852aab7d2298b80878c7719f34ce665725b494d673ecf2e6f9e697564638cc6',
|
||||
'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.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
'org.jacoco:org.jacoco.agent:0.8.3:org.jacoco.agent-0.8.3.jar:522deb254ee16a04cc8341cc8f335f5cb7232982994d961b9cf3a0454709209f',
|
||||
'org.jacoco:org.jacoco.ant:0.8.3:org.jacoco.ant-0.8.3.jar:735844e1ae15f9b875b42a27ac5cb61cc26e106d9e839e5d1c6756709b424ce0',
|
||||
'org.jacoco:org.jacoco.core:0.8.3:org.jacoco.core-0.8.3.jar:0818437bc060a0c7cc798148f22b713702aae2771aba104444407697d578f1ea',
|
||||
'org.jacoco:org.jacoco.report:0.8.3:org.jacoco.report-0.8.3.jar:aae08fa4ff043c807b8876cdb2d8705eb8449a55efce461baa6c09da245088c1',
|
||||
'org.jetbrains.intellij.deps:trove4j:1.0.20181211:trove4j-1.0.20181211.jar:affb7c85a3c87bdcf69ff1dbb84de11f63dc931293934bc08cd7ab18de083601',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145',
|
||||
'org.jacoco:org.jacoco.agent:0.8.7:org.jacoco.agent-0.8.7.jar:9cbcc986e0fbe821a78ff1f8f7d5216f200e5eb124e7f6837d1dc4a77b28b143',
|
||||
'org.jacoco:org.jacoco.ant:0.8.7:org.jacoco.ant-0.8.7.jar:97ca96a382c3f23a44d8eb4c4e6c3742a30cb8005774a76ced0fc4806ce49605',
|
||||
'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.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-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||
'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.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901',
|
||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
|
||||
'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.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
|
||||
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
|
||||
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
|
||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.7.0:junit-jupiter-api-5.7.0.jar:b03f78e0daeed2d77a0af9bcd662b4cdb9693f7ee72e01a539b508b84c63d182',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.7.0:junit-jupiter-engine-5.7.0.jar:dfa26af94644ac2612dde6625852fcb550a0d21caa243257de54cba738ba87af',
|
||||
'org.junit.platform:junit-platform-commons:1.7.0:junit-platform-commons-1.7.0.jar:5330ee87cc7586e6e25175a34e9251624ff12ff525269d3415d0b4ca519b6fea',
|
||||
'org.junit.platform:junit-platform-engine:1.7.0:junit-platform-engine-1.7.0.jar:75f21a20dc594afdc875736725b408cec6d0344874d29f34b2dd3075500236f2',
|
||||
'org.junit.platform:junit-platform-launcher:1.7.0:junit-platform-launcher-1.7.0.jar:fbdc748fde4c4279fe1d3c607447cb3b7ccd45d7338fc574f8a894ddf2d16818',
|
||||
'org.jvnet.staxex:stax-ex:1.8.1:stax-ex-1.8.1.jar:20522549056e9e50aa35ef0b445a2e47a53d06be0b0a9467d704e2483ffb049a',
|
||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||
'org.opentest4j:opentest4j:1.2.0:opentest4j-1.2.0.jar:58812de60898d976fb81ef3b62da05c6604c18fd4a249f5044282479fc286af2',
|
||||
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
|
||||
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
|
||||
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
|
||||
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
|
||||
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
||||
'org.ow2.asm:asm-analysis:9.1:asm-analysis-9.1.jar:81a88041b1b8beda5a8a99646098046c48709538270c49def68abff25ac3be34',
|
||||
'org.ow2.asm:asm-commons:9.1:asm-commons-9.1.jar:afcb26dc1fc12c0c4a99ada670908dd82e18dfc488caf5ee92546996b470c00c',
|
||||
'org.ow2.asm:asm-tree:9.1:asm-tree-9.1.jar:fd00afa49e9595d7646205b09cecb4a776a8ff0ba06f2d59b8f7bf9c704b4a73',
|
||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.testng:testng:7.3.0:testng-7.3.0.jar:63727488f9717d57f0d0a0fee5a1fc10a2be9cfcff2ec3a7187656d663c0774e',
|
||||
'xerces:xercesImpl:2.12.0:xercesImpl-2.12.0.jar:b50d3a4ca502faa4d1c838acb8aa9480446953421f7327e338c5dda3da5e76d0',
|
||||
'xml-apis:xml-apis:1.4.01:xml-apis-1.4.01.jar:a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad',
|
||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ apply from: 'witness.gradle'
|
||||
|
||||
dependencies {
|
||||
api 'org.briarproject:null-safety:0.1'
|
||||
api 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
api 'javax.inject:javax.inject:1'
|
||||
api "com.google.dagger:dagger:$dagger_version"
|
||||
|
||||
implementation "com.google.dagger:dagger:$dagger_version"
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
||||
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@@ -25,7 +26,7 @@ configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
}
|
||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
from sourceSets.test.output
|
||||
from sourceSets.test.output, sourceSets.main.output
|
||||
classifier = 'test'
|
||||
}
|
||||
artifacts {
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||
|
||||
@Immutable
|
||||
@@ -23,17 +24,24 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
|
||||
public abstract class BdfMessageValidator implements MessageValidator {
|
||||
|
||||
protected static final Logger LOG =
|
||||
Logger.getLogger(BdfMessageValidator.class.getName());
|
||||
getLogger(BdfMessageValidator.class.getName());
|
||||
|
||||
protected final ClientHelper clientHelper;
|
||||
protected final MetadataEncoder metadataEncoder;
|
||||
protected final Clock clock;
|
||||
protected final boolean canonical;
|
||||
|
||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
MetadataEncoder metadataEncoder, Clock clock, boolean canonical) {
|
||||
this.clientHelper = clientHelper;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
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,
|
||||
@@ -49,7 +57,7 @@ public abstract class BdfMessageValidator implements MessageValidator {
|
||||
"Timestamp is too far in the future");
|
||||
}
|
||||
try {
|
||||
BdfList bodyList = clientHelper.toList(m.getBody());
|
||||
BdfList bodyList = clientHelper.toList(m, canonical);
|
||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||
return new MessageContext(meta, result.getDependencies());
|
||||
|
||||
@@ -49,6 +49,9 @@ public interface ClientHelper {
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m, boolean canonical)
|
||||
throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
||||
FormatException;
|
||||
|
||||
@@ -106,6 +109,8 @@ public interface ClientHelper {
|
||||
|
||||
BdfList toList(Message m) throws FormatException;
|
||||
|
||||
BdfList toList(Message m, boolean canonical) throws FormatException;
|
||||
|
||||
BdfList toList(Author a);
|
||||
|
||||
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.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();
|
||||
|
||||
@@ -39,9 +39,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -52,19 +52,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
public Boolean getBoolean(String key, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Long getLong(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
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();
|
||||
Long value = getOptionalLong(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -78,20 +75,37 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
public Long getLong(String key, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(String key) throws FormatException {
|
||||
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 {
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
Double value = getOptionalDouble(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -103,17 +117,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(String key, Double defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
public Double getDouble(String key, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public String getString(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
String value = getOptionalString(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -124,17 +137,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
public String getString(String key, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
byte[] value = getOptionalRaw(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -146,17 +158,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key, byte[] defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
public byte[] getRaw(String key, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfList getList(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
BdfList value = getOptionalList(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -167,16 +178,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(String key, BdfList defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
public BdfList getList(String key, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -188,9 +199,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
|
||||
@NotThreadSafe
|
||||
public class BdfList extends ArrayList<Object> {
|
||||
public final class BdfList extends ArrayList<Object> {
|
||||
|
||||
/**
|
||||
* Factory method for constructing lists inline.
|
||||
@@ -33,15 +33,15 @@ public class BdfList extends ArrayList<Object> {
|
||||
super(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean isInRange(int index) {
|
||||
return index >= 0 && index < size();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -53,21 +53,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index, Boolean defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
public Boolean getBoolean(int index, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Long getLong(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
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();
|
||||
Long value = getOptionalLong(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -82,22 +77,37 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(int index, Long defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
public Long getLong(int index, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(int index) throws FormatException {
|
||||
Integer value = getOptionalInt(index);
|
||||
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 {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
Double value = getOptionalDouble(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -110,19 +120,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(int index, Double defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
public Double getDouble(int index, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public String getString(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
String value = getOptionalString(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -134,19 +141,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(int index, String defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
public String getString(int index, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
byte[] value = getOptionalRaw(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -159,19 +163,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index, byte[] defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
public byte[] getRaw(int index, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfList getList(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
BdfList value = getOptionalList(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -183,18 +184,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(int index, BdfList defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
public BdfList getList(int index, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -207,10 +206,9 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,12 @@ public interface BdfReader {
|
||||
|
||||
void skipLong() throws IOException;
|
||||
|
||||
boolean hasInt() throws IOException;
|
||||
|
||||
int readInt() throws IOException;
|
||||
|
||||
void skipInt() throws IOException;
|
||||
|
||||
boolean hasDouble() throws IOException;
|
||||
|
||||
double readDouble() throws IOException;
|
||||
@@ -54,23 +60,11 @@ public interface BdfReader {
|
||||
|
||||
BdfList readList() throws IOException;
|
||||
|
||||
void readListStart() throws IOException;
|
||||
|
||||
boolean hasListEnd() throws IOException;
|
||||
|
||||
void readListEnd() throws IOException;
|
||||
|
||||
void skipList() throws IOException;
|
||||
|
||||
boolean hasDictionary() throws IOException;
|
||||
|
||||
BdfDictionary readDictionary() throws IOException;
|
||||
|
||||
void readDictionaryStart() throws IOException;
|
||||
|
||||
boolean hasDictionaryEnd() throws IOException;
|
||||
|
||||
void readDictionaryEnd() throws IOException;
|
||||
|
||||
void skipDictionary() throws IOException;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ public interface BdfReaderFactory {
|
||||
|
||||
BdfReader createReader(InputStream in);
|
||||
|
||||
BdfReader createReader(InputStream in, boolean canonical);
|
||||
|
||||
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 writeListStart() throws IOException;
|
||||
|
||||
void writeListEnd() throws IOException;
|
||||
|
||||
void writeDictionary(Map<?, ?> m) throws IOException;
|
||||
|
||||
void writeDictionaryStart() throws IOException;
|
||||
|
||||
void writeDictionaryEnd() throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package org.briarproject.bramble.api.keyagreement;
|
||||
|
||||
public interface KeyAgreementConstants {
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
|
||||
/**
|
||||
* The version of the BQP protocol used in beta releases. This version
|
||||
* number is reserved.
|
||||
*/
|
||||
byte BETA_PROTOCOL_VERSION = 89;
|
||||
public interface KeyAgreementConstants {
|
||||
|
||||
/**
|
||||
* The current version of the BQP protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 4;
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish BQP QR codes from
|
||||
* QR codes used for other purposes. See
|
||||
* {@link MailboxConstants#QR_FORMAT_ID}.
|
||||
*/
|
||||
byte QR_FORMAT_ID = 0;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = PROTOCOL_VERSION;
|
||||
|
||||
/**
|
||||
* The length of the BQP key commitment in bytes.
|
||||
*/
|
||||
|
||||
@@ -7,5 +7,5 @@ import java.io.IOException;
|
||||
@NotNullByDefault
|
||||
public interface PayloadParser {
|
||||
|
||||
Payload parse(byte[] raw) throws IOException;
|
||||
Payload parse(String payload) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,6 +20,18 @@ public interface MailboxConstants {
|
||||
*/
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish mailbox QR codes
|
||||
* from QR codes used for other purposes. See
|
||||
* {@link KeyAgreementConstants#QR_FORMAT_ID};
|
||||
*/
|
||||
byte QR_FORMAT_ID = 1;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Mailbox API versions that we support as a client. This is reported to our
|
||||
* contacts by {@link MailboxUpdateManager}.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -34,6 +35,16 @@ public interface MailboxManager {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -1,17 +1,44 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
|
||||
public abstract class MailboxPairingState {
|
||||
|
||||
public static class QrCodeReceived extends MailboxPairingState {
|
||||
public abstract static class Pending extends MailboxPairingState {
|
||||
|
||||
public final long timeStarted;
|
||||
|
||||
private Pending(long timeStarted) {
|
||||
this.timeStarted = timeStarted;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pairing extends MailboxPairingState {
|
||||
public static class QrCodeReceived extends Pending {
|
||||
|
||||
public QrCodeReceived(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pairing extends Pending {
|
||||
|
||||
public Pairing(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Paired extends MailboxPairingState {
|
||||
}
|
||||
|
||||
public static class InvalidQrCode extends MailboxPairingState {
|
||||
|
||||
public final QrCodeType qrCodeType;
|
||||
public final int formatVersion;
|
||||
|
||||
public InvalidQrCode(QrCodeType qrCodeType, int formatVersion) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
this.formatVersion = formatVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
||||
|
||||
@@ -27,8 +27,6 @@ public interface TorConstants {
|
||||
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
||||
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
|
||||
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
||||
// TODO: Remove when settings migration code is removed
|
||||
int PREF_TOR_NETWORK_NEVER = 3;
|
||||
|
||||
// Default values for local settings
|
||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface QrCodeClassifier {
|
||||
|
||||
enum QrCodeType {
|
||||
BQP,
|
||||
MAILBOX,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
Pair<QrCodeType, Integer> classifyQrCode(String payload);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Thrown when a QR code that has been scanned does not have the expected type.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class WrongQrCodeTypeException extends FormatException {
|
||||
|
||||
private final QrCodeType qrCodeType;
|
||||
|
||||
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
}
|
||||
|
||||
public QrCodeType getQrCodeType() {
|
||||
return qrCodeType;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -15,15 +14,21 @@ import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.nio.charset.CodingErrorAction.IGNORE;
|
||||
import static java.nio.charset.CodingErrorAction.REPORT;
|
||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
@NotNullByDefault
|
||||
public class StringUtils {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
private static final Pattern MAC =
|
||||
Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
|
||||
private static final char[] HEX = new char[] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
@@ -45,33 +50,41 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static byte[] toUtf8(String s) {
|
||||
try {
|
||||
return s.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return s.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
public static String fromUtf8(byte[] bytes) {
|
||||
return fromUtf8(bytes, 0, bytes.length);
|
||||
public static String fromUtf8(byte[] bytes) throws FormatException {
|
||||
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();
|
||||
decoder.onMalformedInput(IGNORE);
|
||||
decoder.onUnmappableCharacter(IGNORE);
|
||||
decoder.onMalformedInput(strict ? REPORT : IGNORE);
|
||||
decoder.onUnmappableCharacter(strict ? REPORT : IGNORE);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes, off, len);
|
||||
try {
|
||||
return decoder.decode(buffer).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new AssertionError(e);
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public static String truncateUtf8(String s, int maxUtf8Length) {
|
||||
byte[] utf8 = toUtf8(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -8,9 +9,12 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
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.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BdfDictionaryTest extends BrambleTestCase {
|
||||
@@ -19,20 +23,20 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
public void testConstructors() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
new BdfDictionary());
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(Collections.singletonMap("foo", NULL_VALUE)));
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(singletonMap("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
BdfDictionary.of());
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
public void testLongPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("foo", (byte) 1);
|
||||
d.put("bar", (short) 2);
|
||||
@@ -44,6 +48,33 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
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
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
@@ -67,7 +98,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeySetIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testKeySetIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -86,7 +117,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValuesIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testValuesIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -105,7 +136,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntrySetIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testEntrySetIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -130,4 +161,284 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
assertEquals("d", e.getKey());
|
||||
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.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
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.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class BdfListTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testConstructors() {
|
||||
assertEquals(Collections.emptyList(), new BdfList());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
new BdfList(Arrays.asList(1, 2, NULL_VALUE)));
|
||||
assertEquals(emptyList(), new BdfList());
|
||||
assertEquals(asList(1, 2, NULL_VALUE),
|
||||
new BdfList(asList(1, 2, NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(Collections.emptyList(), BdfList.of());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
BdfList.of(1, 2, NULL_VALUE));
|
||||
assertEquals(emptyList(), BdfList.of());
|
||||
assertEquals(asList(1, 2, NULL_VALUE), BdfList.of(1, 2, NULL_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
public void testLongPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
list.add((byte) 1);
|
||||
list.add((short) 2);
|
||||
@@ -41,6 +41,31 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
@@ -63,61 +88,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testNegativeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -130,6 +100,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(-1, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -142,6 +118,30 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testNegativeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -154,6 +154,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(-1, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -166,6 +172,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(-1, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -178,6 +190,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(-1, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -190,6 +208,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(-1, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDictionaryThrowsFormatException()
|
||||
@@ -203,6 +226,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDictionary(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(-1, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -215,6 +244,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -227,6 +262,30 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testTooLargeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -239,6 +298,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -251,6 +316,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -263,6 +334,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(0, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -275,6 +352,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(0, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDictionaryThrowsFormatException()
|
||||
@@ -287,6 +369,13 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
new BdfList().getOptionalDictionary(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(0, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -299,6 +388,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||
BdfList.of(1.23).getLong(0);
|
||||
@@ -310,6 +405,29 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getDouble(0);
|
||||
@@ -321,6 +439,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getString(0);
|
||||
@@ -332,6 +456,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getRaw(0);
|
||||
@@ -343,6 +473,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testWrongTypeForListThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getList(0);
|
||||
@@ -354,6 +490,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
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)
|
||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||
@@ -366,4 +507,81 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.0:jackson-annotations-2.13.0.jar:81f9724d8843e8b08f8f6c0609e7a2b030d00c34861c4ac7e2099a7235047d6f',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.4:jackson-annotations-2.13.4.jar:ac5b27a634942391ca113850ee7db01df1499a240174021263501c05fc653b44',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
|
||||
@@ -9,30 +9,31 @@ apply from: 'witness.gradle'
|
||||
apply from: '../dagger.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-api', configuration: 'default')
|
||||
implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'
|
||||
api project(':bramble-api')
|
||||
|
||||
api 'org.briarproject:jtorctl:0.5'
|
||||
|
||||
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
implementation 'org.briarproject:jtorctl:0.5'
|
||||
implementation 'org.briarproject:socks-socket:0.1'
|
||||
|
||||
//noinspection GradleDependency
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
|
||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
|
||||
|
||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
@@ -52,7 +53,7 @@ configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
}
|
||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
from sourceSets.test.output
|
||||
from sourceSets.test.output, sourceSets.main.output
|
||||
classifier = 'test'
|
||||
}
|
||||
artifacts {
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
||||
import org.briarproject.bramble.plugin.PluginModule;
|
||||
import org.briarproject.bramble.properties.PropertiesModule;
|
||||
import org.briarproject.bramble.qrcode.QrCodeModule;
|
||||
import org.briarproject.bramble.record.RecordModule;
|
||||
import org.briarproject.bramble.reliability.ReliabilityModule;
|
||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||
@@ -47,6 +48,7 @@ import dagger.Module;
|
||||
MailboxModule.class,
|
||||
PluginModule.class,
|
||||
PropertiesModule.class,
|
||||
QrCodeModule.class,
|
||||
RecordModule.class,
|
||||
ReliabilityModule.class,
|
||||
RendezvousModule.class,
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
@@ -19,9 +20,10 @@ public class TimeLoggingExecutor extends ThreadPoolExecutor {
|
||||
public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
|
||||
long keepAliveTime, TimeUnit unit,
|
||||
BlockingQueue<Runnable> workQueue,
|
||||
ThreadFactory threadFactory,
|
||||
RejectedExecutionHandler handler) {
|
||||
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
|
||||
handler);
|
||||
threadFactory, handler);
|
||||
log = Logger.getLogger(tag);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -29,6 +28,7 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
|
||||
@@ -99,7 +99,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
new FileInputStream(f), UTF_8));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
@@ -151,7 +151,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
@GuardedBy("stateChangeLock")
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes(Charset.forName("UTF-8")));
|
||||
out.write(key.getBytes(UTF_8));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@@ -155,7 +155,13 @@ class ClientHelperImpl implements ClientHelper {
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
||||
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
|
||||
@@ -313,8 +319,13 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
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);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in, canonical);
|
||||
try {
|
||||
BdfList list = reader.readList();
|
||||
if (!reader.eof()) throw new FormatException();
|
||||
@@ -328,7 +339,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b) throws FormatException {
|
||||
return toList(b, 0, b.length);
|
||||
return toList(b, 0, b.length, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,6 +347,12 @@ class ClientHelperImpl implements ClientHelper {
|
||||
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
|
||||
public BdfList toList(Author a) {
|
||||
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
||||
@@ -361,7 +378,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
public Author parseAndValidateAuthor(BdfList author)
|
||||
throws FormatException {
|
||||
checkSize(author, 3);
|
||||
int formatVersion = author.getLong(0).intValue();
|
||||
int formatVersion = author.getInt(0);
|
||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||
String name = author.getString(1);
|
||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
@@ -472,8 +489,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
if (element.size() != 2) {
|
||||
throw new FormatException();
|
||||
}
|
||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
||||
element.getLong(1).intValue()));
|
||||
list.add(new MailboxVersion(element.getInt(0), element.getInt(1)));
|
||||
}
|
||||
// Sort the list of versions for easier comparison
|
||||
sort(list);
|
||||
@@ -486,7 +502,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
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) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
@@ -51,6 +50,7 @@ import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHE
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
@NotNullByDefault
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
@@ -460,7 +460,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
@Override
|
||||
public String encodeOnion(byte[] publicKey) {
|
||||
Digest digest = new SHA3Digest(256);
|
||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
||||
byte[] label = ".onion checksum".getBytes(US_ASCII);
|
||||
digest.update(label, 0, label.length);
|
||||
digest.update(publicKey, 0, publicKey.length);
|
||||
digest.update(ONION_HS_PROTOCOL_VERSION);
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -37,31 +38,31 @@ public class CryptoExecutorModule {
|
||||
private static final int MAX_EXECUTOR_THREADS =
|
||||
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||
|
||||
private final ExecutorService cryptoExecutor;
|
||||
|
||||
public CryptoExecutorModule() {
|
||||
// Use an unbounded queue
|
||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
||||
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@CryptoExecutor
|
||||
ExecutorService provideCryptoExecutorService(
|
||||
LifecycleManager lifecycleManager) {
|
||||
LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
|
||||
// Use an unbounded queue
|
||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||
ExecutorService cryptoExecutor = new TimeLoggingExecutor(
|
||||
"CryptoExecutor", 0, MAX_EXECUTOR_THREADS, 60, SECONDS, queue,
|
||||
threadFactory, policy);
|
||||
lifecycleManager.registerForShutdown(cryptoExecutor);
|
||||
return cryptoExecutor;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@CryptoExecutor
|
||||
Executor provideCryptoExecutor() {
|
||||
Executor provideCryptoExecutor(
|
||||
@CryptoExecutor ExecutorService cryptoExecutor) {
|
||||
return cryptoExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,13 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Scanner;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MessageEncrypter {
|
||||
@@ -228,7 +229,7 @@ public class MessageEncrypter {
|
||||
PublicKey publicKey =
|
||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||
String message = readFully(System.in);
|
||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
||||
byte[] plaintext = message.getBytes(UTF_8);
|
||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||
}
|
||||
@@ -242,7 +243,7 @@ public class MessageEncrypter {
|
||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
||||
System.out.println(new String(plaintext, UTF_8));
|
||||
}
|
||||
|
||||
private static String readFully(InputStream in) throws IOException {
|
||||
|
||||
@@ -18,12 +18,18 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in) {
|
||||
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
|
||||
public BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
|
||||
int maxBufferSize, boolean canonical) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize, canonical);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,21 +33,24 @@ import static org.briarproject.bramble.util.StringUtils.fromUtf8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class BdfReaderImpl implements BdfReader {
|
||||
final class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||
|
||||
private final InputStream in;
|
||||
private final int nestedLimit, maxBufferSize;
|
||||
private final boolean canonical;
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next;
|
||||
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.nestedLimit = nestedLimit;
|
||||
this.maxBufferSize = maxBufferSize;
|
||||
this.canonical = canonical;
|
||||
}
|
||||
|
||||
private void readLookahead() throws IOException {
|
||||
@@ -188,13 +191,22 @@ class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private short readInt16() throws IOException {
|
||||
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 {
|
||||
readIntoBuffer(4);
|
||||
int value = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -202,6 +214,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
readIntoBuffer(8);
|
||||
long value = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -215,6 +232,31 @@ class BdfReaderImpl implements BdfReader {
|
||||
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
|
||||
public boolean hasDouble() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
@@ -323,22 +365,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
private BdfList readList(int level) throws IOException {
|
||||
if (!hasList()) 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasListEnd() throws IOException {
|
||||
return hasEnd();
|
||||
BdfList list = new BdfList();
|
||||
while (!hasEnd()) list.add(readObject(level + 1));
|
||||
readEnd();
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
@@ -347,11 +378,6 @@ class BdfReaderImpl implements BdfReader {
|
||||
return next == END;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if (!hasEnd()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
@@ -361,7 +387,7 @@ class BdfReaderImpl implements BdfReader {
|
||||
public void skipList() throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasListEnd()) skipObject();
|
||||
while (!hasEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@@ -380,35 +406,27 @@ class BdfReaderImpl implements BdfReader {
|
||||
private BdfDictionary readDictionary(int level) throws IOException {
|
||||
if (!hasDictionary()) 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDictionaryEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDictionaryEnd() throws IOException {
|
||||
BdfDictionary dictionary = new BdfDictionary();
|
||||
String prevKey = null;
|
||||
while (!hasEnd()) {
|
||||
String key = readString();
|
||||
if (canonical && prevKey != null && key.compareTo(prevKey) <= 0) {
|
||||
// Keys not unique and sorted
|
||||
throw new FormatException();
|
||||
}
|
||||
dictionary.put(key, readObject(level + 1));
|
||||
prevKey = key;
|
||||
}
|
||||
readEnd();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipDictionary() throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasDictionaryEnd()) {
|
||||
while (!hasEnd()) {
|
||||
skipString();
|
||||
skipObject();
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -15,6 +17,7 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
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.data.Types.DICTIONARY;
|
||||
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_8;
|
||||
import static org.briarproject.bramble.data.Types.TRUE;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class BdfWriterImpl implements BdfWriter {
|
||||
final class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
@@ -113,7 +117,7 @@ class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
out.write(STRING_8);
|
||||
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 byte[]) writeRaw((byte[]) o);
|
||||
else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
|
||||
else if (o instanceof List) writeList((List) o);
|
||||
else if (o instanceof Map) writeDictionary((Map) o);
|
||||
else if (o instanceof List) writeList((List<?>) o);
|
||||
else if (o instanceof Map) writeDictionary((Map<?, ?>) o);
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListStart() throws IOException {
|
||||
out.write(LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionary(Map<?, ?> m) throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
for (Entry<?, ?> e : m.entrySet()) {
|
||||
if (!(e.getKey() instanceof String)) throw new FormatException();
|
||||
writeString((String) e.getKey());
|
||||
writeObject(e.getValue());
|
||||
if (m instanceof BdfDictionary) {
|
||||
// Entries are already sorted and keys are known to be strings
|
||||
for (Entry<String, Object> e : ((BdfDictionary) m).entrySet()) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryStart() throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -28,24 +29,20 @@ public class DatabaseExecutorModule {
|
||||
ExecutorService executorService;
|
||||
}
|
||||
|
||||
private final ExecutorService databaseExecutor;
|
||||
|
||||
public DatabaseExecutorModule() {
|
||||
@Provides
|
||||
@Singleton
|
||||
@DatabaseExecutor
|
||||
ExecutorService provideDatabaseExecutorService(
|
||||
LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
|
||||
// Use an unbounded queue
|
||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Use a single thread and keep it in the pool for 60 secs
|
||||
databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
|
||||
60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@DatabaseExecutor
|
||||
ExecutorService provideDatabaseExecutorService(
|
||||
LifecycleManager lifecycleManager) {
|
||||
ExecutorService databaseExecutor = new TimeLoggingExecutor(
|
||||
"DatabaseExecutor", 0, 1, 60, SECONDS, queue, threadFactory,
|
||||
policy);
|
||||
lifecycleManager.registerForShutdown(databaseExecutor);
|
||||
return databaseExecutor;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.event;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -22,10 +23,11 @@ public class DefaultEventExecutorModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@EventExecutor
|
||||
Executor provideEventExecutor() {
|
||||
Executor provideEventExecutor(ThreadFactory threadFactory) {
|
||||
return newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r);
|
||||
Thread t = threadFactory.newThread(r);
|
||||
t.setDaemon(true);
|
||||
t.setName(t.getName() + "-Event");
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
@@ -19,13 +17,13 @@ public class KeyAgreementModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadEncoder providePayloadEncoder(BdfWriterFactory bdfWriterFactory) {
|
||||
return new PayloadEncoderImpl(bdfWriterFactory);
|
||||
PayloadEncoder providePayloadEncoder(PayloadEncoderImpl payloadEncoder) {
|
||||
return payloadEncoder;
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadParser providePayloadParser(BdfReaderFactory bdfReaderFactory) {
|
||||
return new PayloadParserImpl(bdfReaderFactory);
|
||||
PayloadParser providePayloadParser(PayloadParserImpl payloadParser) {
|
||||
return payloadParser;
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
@@ -13,7 +14,8 @@ import java.io.IOException;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_ID;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -29,14 +31,16 @@ class PayloadEncoderImpl implements PayloadEncoder {
|
||||
@Override
|
||||
public byte[] encode(Payload p) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(PROTOCOL_VERSION);
|
||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||
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);
|
||||
try {
|
||||
w.writeListStart(); // Payload start
|
||||
w.writeRaw(p.getCommitment());
|
||||
for (TransportDescriptor d : p.getTransportDescriptors())
|
||||
w.writeList(d.getDescriptor());
|
||||
w.writeListEnd(); // Payload end
|
||||
w.writeList(payload);
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen with ByteArrayOutputStream
|
||||
throw new AssertionError(e);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
@@ -11,6 +12,9 @@ import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -21,34 +25,42 @@ import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class PayloadParserImpl implements PayloadParser {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory) {
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload parse(byte[] raw) throws IOException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the protocol version
|
||||
int protocolVersion = in.read();
|
||||
if (protocolVersion == -1) throw new FormatException();
|
||||
if (protocolVersion != PROTOCOL_VERSION) {
|
||||
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
|
||||
protocolVersion == BETA_PROTOCOL_VERSION;
|
||||
public Payload parse(String payloadString) throws IOException {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payloadString);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (formatVersion != QR_FORMAT_VERSION) {
|
||||
boolean tooOld = formatVersion < QR_FORMAT_VERSION;
|
||||
throw new UnsupportedVersionException(tooOld);
|
||||
}
|
||||
byte[] raw = payloadString.getBytes(ISO_8859_1);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the format identifier and version (already parsed)
|
||||
if (in.read() == -1) throw new AssertionError();
|
||||
// The rest of the payload is a BDF list with one or more elements
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
BdfList payload = r.readList();
|
||||
@@ -61,7 +73,7 @@ class PayloadParserImpl implements PayloadParser {
|
||||
List<TransportDescriptor> recognised = new ArrayList<>();
|
||||
for (int i = 1; i < payload.size(); i++) {
|
||||
BdfList descriptor = payload.getList(i);
|
||||
long transportId = descriptor.getLong(0);
|
||||
int transportId = descriptor.getInt(0);
|
||||
if (transportId == TRANSPORT_ID_BLUETOOTH) {
|
||||
TransportId id = BluetoothConstants.ID;
|
||||
recognised.add(new TransportDescriptor(id, descriptor));
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -28,19 +29,6 @@ public class LifecycleModule {
|
||||
Executor executor;
|
||||
}
|
||||
|
||||
private final ExecutorService ioExecutor;
|
||||
|
||||
public LifecycleModule() {
|
||||
// The thread pool is unbounded, so use direct handoff
|
||||
BlockingQueue<Runnable> queue = new SynchronousQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create threads as required and keep them in the pool for 60 seconds
|
||||
ioExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||
60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ShutdownManager provideShutdownManager() {
|
||||
@@ -57,7 +45,16 @@ public class LifecycleModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@IoExecutor
|
||||
Executor provideIoExecutor(LifecycleManager lifecycleManager) {
|
||||
Executor provideIoExecutor(LifecycleManager lifecycleManager,
|
||||
ThreadFactory threadFactory) {
|
||||
// The thread pool is unbounded, so use direct handoff
|
||||
BlockingQueue<Runnable> queue = new SynchronousQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create threads as required and keep them in the pool for 60 seconds
|
||||
ExecutorService ioExecutor = new ThreadPoolExecutor(0,
|
||||
Integer.MAX_VALUE, 60, SECONDS, queue, threadFactory, policy);
|
||||
lifecycleManager.registerForShutdown(ioExecutor);
|
||||
return ioExecutor;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,15 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final MailboxConfig mailboxConfig;
|
||||
private final Executor ioExecutor;
|
||||
|
||||
@Inject
|
||||
MailboxApiCallerImpl(TaskScheduler taskScheduler,
|
||||
MailboxConfig mailboxConfig,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.mailboxConfig = mailboxConfig;
|
||||
this.ioExecutor = ioExecutor;
|
||||
}
|
||||
|
||||
@@ -49,7 +52,8 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
private boolean cancelled = false;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private long retryIntervalMs = MIN_RETRY_INTERVAL_MS;
|
||||
private long retryIntervalMs =
|
||||
mailboxConfig.getApiCallerMinRetryInterval();
|
||||
|
||||
private Task(ApiCall apiCall) {
|
||||
this.apiCall = apiCall;
|
||||
@@ -74,8 +78,9 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
scheduledTask = taskScheduler.schedule(this::callApi,
|
||||
ioExecutor, retryIntervalMs, MILLISECONDS);
|
||||
// Increase the retry interval each time we retry
|
||||
retryIntervalMs =
|
||||
min(MAX_RETRY_INTERVAL_MS, retryIntervalMs * 2);
|
||||
retryIntervalMs = min(
|
||||
mailboxConfig.getApiCallerMaxRetryInterval(),
|
||||
retryIntervalMs * 2);
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
|
||||
interface MailboxConfig {
|
||||
|
||||
/**
|
||||
* The minimum interval between API call retries in milliseconds.
|
||||
*/
|
||||
long getApiCallerMinRetryInterval();
|
||||
|
||||
/**
|
||||
* The maximum interval between API call retries in milliseconds.
|
||||
*/
|
||||
long getApiCallerMaxRetryInterval();
|
||||
|
||||
/**
|
||||
* How long (in milliseconds) the Tor plugin needs to be continuously
|
||||
* {@link Plugin.State#ACTIVE active} before we assume our contacts can
|
||||
* reach our hidden service.
|
||||
*/
|
||||
long getTorReachabilityPeriod();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class MailboxConfigImpl implements MailboxConfig {
|
||||
|
||||
@Inject
|
||||
MailboxConfigImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getApiCallerMinRetryInterval() {
|
||||
return MailboxApiCaller.MIN_RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getApiCallerMaxRetryInterval() {
|
||||
return MailboxApiCaller.MAX_RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTorReachabilityPeriod() {
|
||||
return TorReachabilityMonitor.REACHABILITY_PERIOD_MS;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
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.MailboxVersion;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -98,6 +103,22 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
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
|
||||
public boolean checkConnection() {
|
||||
List<MailboxVersion> versions = null;
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -25,6 +26,7 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
MailboxPairingTaskFactoryImpl(
|
||||
@@ -34,7 +36,8 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
this.crypto = crypto;
|
||||
@@ -42,12 +45,13 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
|
||||
crypto, clock, api, mailboxSettingsManager,
|
||||
mailboxUpdateManager);
|
||||
mailboxUpdateManager, qrCodeClassifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -9,18 +10,26 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -32,7 +41,10 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
@@ -40,9 +52,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
private final static Logger LOG =
|
||||
getLogger(MailboxPairingTaskImpl.class.getName());
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
private static final int VERSION_REQUIRED = 32;
|
||||
|
||||
private final String payload;
|
||||
private final Executor eventExecutor;
|
||||
@@ -52,6 +61,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
private final long timeStarted;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
@@ -68,7 +79,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.payload = payload;
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
@@ -77,7 +89,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
state = new MailboxPairingState.QrCodeReceived();
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
timeStarted = clock.currentTimeMillis();
|
||||
state = new QrCodeReceived(timeStarted);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,22 +113,30 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payload);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (qrCodeType != MAILBOX || formatVersion != QR_FORMAT_VERSION) {
|
||||
setState(new InvalidQrCode(qrCodeType, formatVersion));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
pairMailbox();
|
||||
} catch (FormatException e) {
|
||||
onMailboxError(e, new MailboxPairingState.InvalidQrCode());
|
||||
onMailboxError(e, new InvalidQrCode(qrCodeType, formatVersion));
|
||||
} catch (MailboxAlreadyPairedException e) {
|
||||
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
|
||||
onMailboxError(e, new MailboxAlreadyPaired());
|
||||
} catch (IOException e) {
|
||||
onMailboxError(e, new MailboxPairingState.ConnectionError());
|
||||
onMailboxError(e, new ConnectionError());
|
||||
} catch (ApiException | DbException e) {
|
||||
onMailboxError(e, new MailboxPairingState.UnexpectedError());
|
||||
onMailboxError(e, new UnexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
private void pairMailbox() throws IOException, ApiException, DbException {
|
||||
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
|
||||
setState(new MailboxPairingState.Pairing());
|
||||
setState(new Pairing(timeStarted));
|
||||
MailboxProperties ownerProperties = api.setup(mailboxProperties);
|
||||
long time = clock.currentTimeMillis();
|
||||
db.transaction(false, txn -> {
|
||||
@@ -133,7 +155,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
}
|
||||
});
|
||||
setState(new MailboxPairingState.Paired());
|
||||
setState(new Paired());
|
||||
}
|
||||
|
||||
private void onMailboxError(Exception e, MailboxPairingState state) {
|
||||
@@ -167,14 +189,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
int version = bytes[0] & 0xFF;
|
||||
if (version != VERSION_REQUIRED) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("QR code has not version " + VERSION_REQUIRED +
|
||||
": " + version);
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
LOG.info("QR code is valid");
|
||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||
String onion = crypto.encodeOnion(onionPubKey);
|
||||
|
||||
@@ -4,7 +4,11 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class UrlConverterModule {
|
||||
public class ModularMailboxModule {
|
||||
@Provides
|
||||
MailboxConfig provideMailboxConfig(MailboxConfigImpl mailboxConfig) {
|
||||
return mailboxConfig;
|
||||
}
|
||||
|
||||
@Provides
|
||||
UrlConverter provideUrlConverter(UrlConverterImpl urlConverter) {
|
||||
@@ -32,6 +32,7 @@ class TorReachabilityMonitorImpl
|
||||
|
||||
private final Executor ioExecutor;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final MailboxConfig mailboxConfig;
|
||||
private final PluginManager pluginManager;
|
||||
private final EventBus eventBus;
|
||||
private final Object lock = new Object();
|
||||
@@ -50,10 +51,12 @@ class TorReachabilityMonitorImpl
|
||||
TorReachabilityMonitorImpl(
|
||||
@IoExecutor Executor ioExecutor,
|
||||
TaskScheduler taskScheduler,
|
||||
MailboxConfig mailboxConfig,
|
||||
PluginManager pluginManager,
|
||||
EventBus eventBus) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.mailboxConfig = mailboxConfig;
|
||||
this.pluginManager = pluginManager;
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
@@ -110,7 +113,7 @@ class TorReachabilityMonitorImpl
|
||||
synchronized (lock) {
|
||||
if (destroyed || task != null) return;
|
||||
task = taskScheduler.schedule(this::onTorReachable, ioExecutor,
|
||||
REACHABILITY_PERIOD_MS, MILLISECONDS);
|
||||
mailboxConfig.getTorReachabilityPeriod(), MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,17 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
|
||||
private volatile String contactConnectionsUuid = null;
|
||||
|
||||
/**
|
||||
* Override and return true, if the plugin is now allowed to access the
|
||||
* Bluetooth hardware.
|
||||
* If this returns false, the plugin must be
|
||||
* {@link org.briarproject.bramble.api.plugin.Plugin.State#DISABLED}
|
||||
* in {@link #start()} and not attempt to access Bluetooth hardware.
|
||||
*/
|
||||
protected boolean isBluetoothAccessible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract void initialiseAdapter() throws IOException;
|
||||
|
||||
abstract boolean isAdapterEnabled();
|
||||
@@ -176,19 +187,28 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
|
||||
DEFAULT_PREF_EVER_CONNECTED));
|
||||
// disable plugin, if conditions for enabling are not met
|
||||
if (enabledByUser && !isBluetoothAccessible()) {
|
||||
enabledByUser = false;
|
||||
settings.putBoolean(PREF_PLUGIN_ENABLE, false);
|
||||
callback.mergeSettings(settings);
|
||||
}
|
||||
state.setStarted(enabledByUser);
|
||||
try {
|
||||
initialiseAdapter();
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
updateProperties();
|
||||
if (enabledByUser && isAdapterEnabled()) bind();
|
||||
if (enabledByUser) {
|
||||
updateProperties();
|
||||
if (isAdapterEnabled()) bind();
|
||||
}
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
ioExecutor.execute(() -> {
|
||||
if (getState() != INACTIVE) return;
|
||||
if (contactConnectionsUuid == null) updateProperties();
|
||||
// Bind a server socket to accept connections from contacts
|
||||
SS ss;
|
||||
try {
|
||||
@@ -534,7 +554,8 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
private void onSettingsUpdated(Settings settings) {
|
||||
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
SS ss = state.setEnabledByUser(enabledByUser);
|
||||
boolean shouldEnable = enabledByUser && isBluetoothAccessible();
|
||||
SS ss = state.setEnabledByUser(shouldEnable);
|
||||
State s = getState();
|
||||
if (ss != null) {
|
||||
LOG.info("Disabled by user, closing server socket");
|
||||
|
||||
@@ -419,7 +419,7 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
private InetSocketAddress parseSocketAddress(BdfList descriptor)
|
||||
throws FormatException {
|
||||
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();
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByAddress(address);
|
||||
|
||||
@@ -46,7 +46,6 @@ import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -100,6 +99,7 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
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;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -140,7 +140,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
private final long maxLatency;
|
||||
private final int maxIdleTime;
|
||||
private final int socketTimeout;
|
||||
private final File torDirectory, geoIpFile, configFile;
|
||||
private final File torDirectory;
|
||||
private final File configFile;
|
||||
private final int torSocksPort;
|
||||
private final int torControlPort;
|
||||
private final File doneFile, cookieFile;
|
||||
@@ -195,7 +196,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
this.torDirectory = torDirectory;
|
||||
this.torSocksPort = torSocksPort;
|
||||
this.torControlPort = torControlPort;
|
||||
geoIpFile = new File(torDirectory, "geoip");
|
||||
configFile = new File(torDirectory, "torrc");
|
||||
doneFile = new File(torDirectory, "done");
|
||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||
@@ -332,12 +332,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
// The done file may already exist from a previous installation
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
doneFile.delete();
|
||||
// The GeoIP file may exist from a previous installation - we can
|
||||
// save some space by deleting it.
|
||||
// TODO: Remove after a reasonable migration period
|
||||
// (added 2022-03-29)
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
geoIpFile.delete();
|
||||
installTorExecutable();
|
||||
installObfs4Executable();
|
||||
installSnowflakeExecutable();
|
||||
@@ -419,9 +413,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new ByteArrayInputStream(
|
||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||
return new ByteArrayInputStream(strb.toString().getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private void listFiles(File f) {
|
||||
@@ -811,7 +803,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
@Override
|
||||
public void controlConnectionClosed() {
|
||||
if (state.isTorRunning()) {
|
||||
throw new RuntimeException("Control connection closed");
|
||||
// TODO: Restart the Tor process
|
||||
LOG.warning("Control connection closed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
|
||||
@@ -31,6 +31,6 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
||||
byte[] hash = spec.getH();
|
||||
byte[] base64 = Base64.encode(hash);
|
||||
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII"));
|
||||
return "ED25519-V3:" + new String(base64, US_ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
// Retrieve and parse the latest local properties
|
||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
e.getValue().messageId);
|
||||
e.getValue().messageId, false);
|
||||
local.put(e.getKey(), parseProperties(message));
|
||||
}
|
||||
return local;
|
||||
@@ -222,7 +222,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest local properties
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
p = parseProperties(message);
|
||||
}
|
||||
return p == null ? new TransportProperties() : p;
|
||||
@@ -252,7 +252,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
local = new TransportProperties();
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
local = parseProperties(message);
|
||||
}
|
||||
storeLocalProperties(txn, c, t, local);
|
||||
@@ -272,8 +272,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
remote = new TransportProperties();
|
||||
} else {
|
||||
// Retrieve and parse the latest remote properties
|
||||
BdfList message =
|
||||
clientHelper.getMessageAsList(txn, latest.messageId);
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
remote = parseProperties(message);
|
||||
}
|
||||
// Merge in any discovered properties
|
||||
@@ -317,7 +317,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
changed = true;
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
TransportProperties old = parseProperties(message);
|
||||
merged = new TransportProperties(old);
|
||||
for (Entry<String, String> e : p.entrySet()) {
|
||||
|
||||
@@ -27,7 +27,10 @@ class TransportPropertyValidator extends BdfMessageValidator {
|
||||
|
||||
TransportPropertyValidator(ClientHelper clientHelper,
|
||||
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
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class QrCodeClassifierImpl implements QrCodeClassifier {
|
||||
|
||||
@Inject
|
||||
QrCodeClassifierImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<QrCodeType, Integer> classifyQrCode(String payload) {
|
||||
byte[] bytes = payload.getBytes(ISO_8859_1);
|
||||
if (bytes.length == 0) return new Pair<>(UNKNOWN, 0);
|
||||
// If this is a Bramble QR code then the first byte encodes the
|
||||
// format ID (3 bits) and version (5 bits)
|
||||
int formatIdAndVersion = bytes[0] & 0xFF;
|
||||
int formatId = formatIdAndVersion >> 5;
|
||||
int formatVersion = formatIdAndVersion & 0x1F;
|
||||
if (formatId == KeyAgreementConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(BQP, formatVersion);
|
||||
}
|
||||
if (formatId == MailboxConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(MAILBOX, formatVersion);
|
||||
}
|
||||
return new Pair<>(UNKNOWN, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class QrCodeModule {
|
||||
|
||||
@Provides
|
||||
QrCodeClassifier provideQrCodeClassifier(
|
||||
QrCodeClassifierImpl qrCodeClassifier) {
|
||||
return qrCodeClassifier;
|
||||
}
|
||||
}
|
||||
@@ -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.REQUEST;
|
||||
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.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
||||
@@ -126,6 +127,8 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
byte[] payload = nextRecord.getPayload();
|
||||
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||
throw new FormatException();
|
||||
if (payload.length > MAX_MESSAGE_LENGTH)
|
||||
throw new FormatException();
|
||||
// Validate timestamp
|
||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||
if (timestamp < 0) throw new FormatException();
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -21,18 +22,15 @@ public class DefaultTaskSchedulerModule {
|
||||
TaskScheduler scheduler;
|
||||
}
|
||||
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
public DefaultTaskSchedulerModule() {
|
||||
@Provides
|
||||
@Singleton
|
||||
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager,
|
||||
ThreadFactory threadFactory) {
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ScheduledThreadPoolExecutor.DiscardPolicy();
|
||||
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager) {
|
||||
ScheduledExecutorService scheduledExecutorService =
|
||||
new ScheduledThreadPoolExecutor(1, threadFactory, policy);
|
||||
lifecycleManager.registerForShutdown(scheduledExecutorService);
|
||||
return new TaskSchedulerImpl(scheduledExecutorService);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class DefaultThreadFactoryModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
ThreadFactory provideThreadFactory() {
|
||||
return Executors.defaultThreadFactory();
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,7 @@ class SessionParserImpl implements SessionParser {
|
||||
|
||||
@Override
|
||||
public Session parseSession(BdfDictionary meta) throws FormatException {
|
||||
State state =
|
||||
State.fromValue(meta.getLong(SESSION_KEY_STATE).intValue());
|
||||
State state = State.fromValue(meta.getInt(SESSION_KEY_STATE));
|
||||
|
||||
MessageId lastLocalMessageId = null;
|
||||
byte[] lastLocalMessageIdBytes =
|
||||
@@ -56,9 +55,9 @@ class SessionParserImpl implements SessionParser {
|
||||
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
||||
|
||||
KeySetId keySetId = null;
|
||||
Long keySetIdLong = meta.getOptionalLong(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdLong != null) {
|
||||
keySetId = new KeySetId(keySetIdLong.intValue());
|
||||
Integer keySetIdInt = meta.getOptionalInt(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdInt != null) {
|
||||
keySetId = new KeySetId(keySetIdInt);
|
||||
}
|
||||
|
||||
return new Session(state, lastLocalMessageId, localKeyPair,
|
||||
|
||||
@@ -177,8 +177,8 @@ class TransportKeyAgreementManagerImpl extends BdfIncomingMessageHook
|
||||
protected DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||
BdfList body, BdfDictionary meta)
|
||||
throws DbException, FormatException {
|
||||
MessageType type = MessageType.fromValue(
|
||||
meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
|
||||
MessageType type =
|
||||
MessageType.fromValue(meta.getInt(MSG_KEY_MESSAGE_TYPE));
|
||||
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Received " + type + " message for " + t);
|
||||
|
||||
@@ -42,7 +42,7 @@ class TransportKeyAgreementValidator extends BdfMessageValidator {
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
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);
|
||||
else if (type == ACTIVATE) return validateActivateMessage(body);
|
||||
else throw new AssertionError();
|
||||
|
||||
@@ -228,15 +228,11 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
Contact contact = db.getContact(txn, c);
|
||||
callVisibilityHooks(txn, contact, before, after);
|
||||
}
|
||||
// Broadcast events for any new client versions
|
||||
Set<ClientVersion> oldRemoteVersions = new HashSet<>();
|
||||
for (ClientState cs : oldRemoteStates) {
|
||||
oldRemoteVersions.add(cs.clientVersion);
|
||||
}
|
||||
// Broadcast events for any client version update
|
||||
for (ClientState cs : newRemoteStates) {
|
||||
if (!oldRemoteVersions.contains(cs.clientVersion)) {
|
||||
txn.attach(new ClientVersionUpdatedEvent(c,
|
||||
cs.clientVersion));
|
||||
if (!oldRemoteStates.contains(cs)) {
|
||||
txn.attach(
|
||||
new ClientVersionUpdatedEvent(c, cs.clientVersion));
|
||||
}
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
@@ -305,8 +301,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
for (int i = 0; i < size; i++) {
|
||||
BdfList cv = body.getList(i);
|
||||
ClientId clientId = new ClientId(cv.getString(0));
|
||||
int majorVersion = cv.getLong(1).intValue();
|
||||
int minorVersion = cv.getLong(2).intValue();
|
||||
int majorVersion = cv.getInt(1);
|
||||
int minorVersion = cv.getInt(2);
|
||||
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion));
|
||||
}
|
||||
return parsed;
|
||||
@@ -412,8 +408,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
throws FormatException {
|
||||
// Client ID, major version, minor version, active
|
||||
ClientId clientId = new ClientId(clientState.getString(0));
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
int majorVersion = clientState.getInt(1);
|
||||
int minorVersion = clientState.getInt(2);
|
||||
boolean active = clientState.getBoolean(3);
|
||||
return new ClientState(clientId, majorVersion, minorVersion, active);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ class ClientVersioningValidator extends BdfMessageValidator {
|
||||
checkSize(clientState, 4);
|
||||
String clientId = clientState.getString(0);
|
||||
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
int majorVersion = clientState.getInt(1);
|
||||
if (majorVersion < 0) throw new FormatException();
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
int minorVersion = clientState.getInt(2);
|
||||
if (minorVersion < 0) throw new FormatException();
|
||||
clientState.getBoolean(3);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=
|
||||
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 207.154.242.137:80 8E67A1B2A342652EE27376BD61BECF5806700E7F cert=qUrR9fan3XPNGNOwn9WGlXLJNZZx0grXH4AZXR+yoBbtbbj5Ak1n4a7TtjYgXcWcs/gHXw 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 15.235.47.204:42058 869133925B3CD07683BDF01805C36448D090CE88 cert=PFwh4mzZlSTUdcEskpe20t998n5jbr81s+XoX7gmazqzUGHNhkendK5K1j2gOxesz9AkBw iat-mode=0
|
||||
n Bridge obfs4 51.75.93.136:45532 8402B84833527BC249B21AC885134197E624FB5A cert=LwXEf/Dgo0tKdMJByXdlvWiJqyyPw4T284Cg5qygDuIJJNFuz3ED9UhGil6H4Of3gM7wSg 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
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -34,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -342,7 +342,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
private void storeDatabaseKey(File f, String hex) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(hex.getBytes(Charset.forName("UTF-8")));
|
||||
out.write(hex.getBytes(UTF_8));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
@@ -350,7 +350,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
@Nullable
|
||||
private String loadDatabaseKey(File f) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
new FileInputStream(f), UTF_8));
|
||||
String hex = reader.readLine();
|
||||
reader.close();
|
||||
return hex;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -86,7 +86,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(shortMessage.getBody());
|
||||
oneOf(clientHelper).toList(shortMessage, true);
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -114,7 +114,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(throwException(new FormatException()));
|
||||
}});
|
||||
|
||||
@@ -126,7 +126,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(returnValue(body));
|
||||
}});
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory)
|
||||
.createReader(with(any(InputStream.class)));
|
||||
.createReader(with(any(InputStream.class)), with(true));
|
||||
will(returnValue(bdfReader));
|
||||
oneOf(bdfReader).readList();
|
||||
will(returnValue(list));
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.mailbox.UrlConverterModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
@@ -25,7 +25,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class,
|
||||
UrlConverterModule.class,
|
||||
ModularMailboxModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -31,11 +32,14 @@ public class BdfReaderImplFuzzingTest extends BrambleTestCase {
|
||||
buf[1] = 0x14; // Length 20 bytes
|
||||
in.reset();
|
||||
BdfReaderImpl r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
int length = r.readString().length();
|
||||
assertTrue(length >= 0);
|
||||
assertTrue(length <= 20);
|
||||
assertTrue(r.eof());
|
||||
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||
try {
|
||||
int length = r.readString().length();
|
||||
assertTrue(length <= 20);
|
||||
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.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||
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.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
@@ -88,6 +89,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipLong16() throws Exception {
|
||||
setContents("22" + "0080");
|
||||
@@ -106,6 +119,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipLong32() throws Exception {
|
||||
setContents("24" + "00008000");
|
||||
@@ -124,13 +149,48 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipLong() throws Exception {
|
||||
public void testSkipLong64() throws Exception {
|
||||
setContents("28" + "0000000080000000");
|
||||
r.skipLong();
|
||||
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
|
||||
public void testReadDouble() throws Exception {
|
||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||
@@ -162,7 +222,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString8() throws Exception {
|
||||
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
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
@@ -186,7 +246,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testSkipString8() throws Exception {
|
||||
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
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
@@ -199,9 +259,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString16() throws Exception {
|
||||
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 longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// 128 random letters and 2^15 -1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
assertEquals(shortest, r.readString());
|
||||
@@ -213,7 +273,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
public void testReadString16ChecksMaxLength() throws Exception {
|
||||
int maxBufferSize = 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";
|
||||
// 128 random letters, the same plus a space
|
||||
setContents("42" + "0080" + validHex
|
||||
@@ -223,12 +283,20 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipString16() throws Exception {
|
||||
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 longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// 128 random letters and 2^15 - 1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
r.skipString();
|
||||
@@ -239,7 +307,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString32() throws Exception {
|
||||
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
|
||||
setContents("44" + "00008000" + shortHex);
|
||||
assertEquals(shortest, r.readString());
|
||||
@@ -250,7 +318,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
public void testReadString32ChecksMaxLength() throws Exception {
|
||||
int maxBufferSize = Short.MAX_VALUE + 1;
|
||||
String valid = getRandomString(maxBufferSize);
|
||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
||||
String validHex = toHexString(valid.getBytes(UTF_8));
|
||||
String invalidHex = validHex + "20";
|
||||
// 2^15 random letters, the same plus a space
|
||||
setContents("44" + "00008000" + validHex +
|
||||
@@ -260,10 +328,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipString32() throws Exception {
|
||||
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
|
||||
setContents("44" + "00008000" + shortHex +
|
||||
"44" + "00008000" + shortHex);
|
||||
@@ -275,7 +351,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadUtf8String() throws Exception {
|
||||
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
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
|
||||
assertEquals("foo", r.readString());
|
||||
@@ -348,6 +424,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipRaw16() throws Exception {
|
||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||
@@ -385,6 +469,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipRaw32() throws Exception {
|
||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||
@@ -434,25 +526,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipList() throws Exception {
|
||||
// A list containing 1, "foo", and 128
|
||||
@@ -465,9 +538,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDictionary() throws Exception {
|
||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
||||
setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
|
||||
"41" + "03" + "626172" + "00" + "80");
|
||||
// A dictionary containing "bar" -> null and "foo" -> 123
|
||||
setContents("70" + "41" + "03" + "626172" + "00" +
|
||||
"41" + "03" + "666F6F" + "21" + "7B" + "80");
|
||||
BdfDictionary dictionary = r.readDictionary();
|
||||
assertEquals(2, dictionary.size());
|
||||
assertTrue(dictionary.containsKey("foo"));
|
||||
@@ -517,26 +590,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
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
|
||||
public void testSkipDictionary() throws Exception {
|
||||
// A map containing "foo" -> 123 and "bar" -> null
|
||||
@@ -557,10 +610,10 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testNestedListWithinDepthLimit() throws Exception {
|
||||
// A list containing a list containing a list containing a list...
|
||||
String lists = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "60";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "80";
|
||||
setContents(lists);
|
||||
StringBuilder lists = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("60");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("80");
|
||||
setContents(lists.toString());
|
||||
r.readList();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
@@ -568,23 +621,25 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNestedListOutsideDepthLimit() throws Exception {
|
||||
// A list containing a list containing a list containing a list...
|
||||
String lists = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "60";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "80";
|
||||
setContents(lists);
|
||||
StringBuilder lists = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("60");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("80");
|
||||
setContents(lists.toString());
|
||||
r.readList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedDictionaryWithinDepthLimit() throws Exception {
|
||||
// A dictionary containing a dictionary containing a dictionary...
|
||||
String dicts = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
||||
dicts += "70" + "41" + "03" + "666F6F";
|
||||
dicts += "11";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
||||
dicts += "80";
|
||||
setContents(dicts);
|
||||
StringBuilder dicts = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||
dicts.append("70").append("41").append("03").append("666F6F");
|
||||
}
|
||||
dicts.append("11");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||
dicts.append("80");
|
||||
}
|
||||
setContents(dicts.toString());
|
||||
r.readDictionary();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
@@ -592,13 +647,15 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNestedDictionaryOutsideDepthLimit() throws Exception {
|
||||
// A dictionary containing a dictionary containing a dictionary...
|
||||
String dicts = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
||||
dicts += "70" + "41" + "03" + "666F6F";
|
||||
dicts += "11";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
||||
dicts += "80";
|
||||
setContents(dicts);
|
||||
StringBuilder dicts = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||
dicts.append("70").append("41").append("03").append("666F6F");
|
||||
}
|
||||
dicts.append("11");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||
dicts.append("80");
|
||||
}
|
||||
setContents(dicts.toString());
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@@ -625,6 +682,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
private void setContents(String hex, int maxBufferSize)
|
||||
throws FormatException {
|
||||
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;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
@@ -168,9 +169,11 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
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<>();
|
||||
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);
|
||||
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||
@@ -180,30 +183,17 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDelimitedList() throws IOException {
|
||||
w.writeListStart();
|
||||
w.writeLong(1);
|
||||
w.writeString("foo");
|
||||
w.writeLong(128);
|
||||
w.writeListEnd();
|
||||
// LIST tag, 1 as integer, "foo" as string, 128 as integer, END tag
|
||||
checkContents("60" + "21" + "01" +
|
||||
"41" + "03" + "666F6F" +
|
||||
"22" + "0080" + "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");
|
||||
public void testWriteBdfDictionary() throws IOException {
|
||||
// Add entries to dictionary in descending order - they should be
|
||||
// output in ascending order
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
for (int i = 3; i >= 0; i--) d.put(String.valueOf(i), i);
|
||||
w.writeDictionary(d);
|
||||
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||
"41" + "01" + "31" + "21" + "01" +
|
||||
"41" + "01" + "32" + "21" + "02" +
|
||||
"41" + "01" + "33" + "21" + "03" + "80");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -2,21 +2,27 @@ package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -26,32 +32,29 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory =
|
||||
context.mock(BdfReaderFactory.class);
|
||||
private final QrCodeClassifier qrCodeClassifier =
|
||||
context.mock(QrCodeClassifier.class);
|
||||
private final BdfReader bdfReader = context.mock(BdfReader.class);
|
||||
|
||||
private final PayloadParserImpl payloadParser =
|
||||
new PayloadParserImpl(bdfReaderFactory);
|
||||
private final String payload = getRandomString(123);
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testThrowsFormatExceptionIfPayloadIsEmpty() throws Exception {
|
||||
payloadParser.parse(new byte[0]);
|
||||
private final PayloadParserImpl payloadParser =
|
||||
new PayloadParserImpl(bdfReaderFactory, qrCodeClassifier);
|
||||
|
||||
@Test(expected = WrongQrCodeTypeException.class)
|
||||
public void testThrowsExceptionForWrongQrCodeType() throws Exception {
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForOldVersion()
|
||||
throws Exception {
|
||||
try {
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION - 1});
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertTrue(e.isTooOld());
|
||||
}
|
||||
}
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION - 1);
|
||||
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForBetaVersion()
|
||||
throws Exception {
|
||||
try {
|
||||
payloadParser.parse(new byte[] {BETA_PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertTrue(e.isTooOld());
|
||||
@@ -61,8 +64,10 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForNewVersion()
|
||||
throws Exception {
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION + 1);
|
||||
|
||||
try {
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION + 1});
|
||||
payloadParser.parse(payload);
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertFalse(e.isTooOld());
|
||||
@@ -71,6 +76,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testThrowsFormatExceptionForEmptyList() throws Exception {
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -79,7 +86,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(new BdfList()));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -87,6 +94,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -97,7 +106,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(false));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -105,6 +114,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -115,7 +125,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -123,6 +133,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -133,12 +144,14 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsPayloadWithNoDescriptors() throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -149,8 +162,16 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
Payload p = payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
Payload p = payloadParser.parse(payload);
|
||||
assertArrayEquals(commitment, p.getCommitment());
|
||||
assertTrue(p.getTransportDescriptors().isEmpty());
|
||||
}
|
||||
|
||||
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||
int formatVersion) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@ public class MailboxApiCallerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final TaskScheduler taskScheduler =
|
||||
context.mock(TaskScheduler.class);
|
||||
private final MailboxConfig mailboxConfig = new MailboxConfigImpl();
|
||||
private final Executor ioExecutor = context.mock(Executor.class);
|
||||
private final ApiCall apiCall = context.mock(ApiCall.class);
|
||||
private final Cancellable scheduledTask = context.mock(Cancellable.class);
|
||||
|
||||
private final MailboxApiCallerImpl caller =
|
||||
new MailboxApiCallerImpl(taskScheduler, ioExecutor);
|
||||
new MailboxApiCallerImpl(taskScheduler, mailboxConfig, ioExecutor);
|
||||
|
||||
@Test
|
||||
public void testSubmitsTaskImmediately() {
|
||||
|
||||
@@ -24,9 +24,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
@@ -34,8 +31,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import okio.Buffer;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.mailbox.MailboxTestUtils.createHttpClientProvider;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
@@ -56,18 +53,8 @@ public class MailboxApiTest extends BrambleTestCase {
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.socketFactory(SocketFactory.getDefault())
|
||||
.connectTimeout(60_000, MILLISECONDS)
|
||||
.build();
|
||||
private final WeakSingletonProvider<OkHttpClient> httpClientProvider =
|
||||
new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public OkHttpClient createInstance() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
createHttpClientProvider();
|
||||
// We aren't using a real onion address, so use the given address verbatim
|
||||
private final UrlConverter urlConverter = onion -> onion;
|
||||
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
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.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
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.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
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.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class MailboxManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -125,4 +132,32 @@ public class MailboxManagerImplTest extends BrambleMockTestCase {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -7,13 +8,24 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
@@ -22,13 +34,15 @@ import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
@@ -49,9 +63,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
context.mock(MailboxSettingsManager.class);
|
||||
private final MailboxUpdateManager mailboxUpdateManager =
|
||||
context.mock(MailboxUpdateManager.class);
|
||||
private final MailboxPairingTaskFactory factory =
|
||||
new MailboxPairingTaskFactoryImpl(executor, db, crypto, clock, api,
|
||||
mailboxSettingsManager, mailboxUpdateManager);
|
||||
private final QrCodeClassifier qrCodeClassifier =
|
||||
context.mock(QrCodeClassifier.class);
|
||||
|
||||
private final String onion = getRandomString(56);
|
||||
private final byte[] onionBytes = getRandomBytes(32);
|
||||
@@ -59,7 +72,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
new MailboxAuthToken(getRandomId());
|
||||
private final MailboxAuthToken ownerToken =
|
||||
new MailboxAuthToken(getRandomId());
|
||||
private final String validPayload = getValidPayload();
|
||||
private final String validPayload =
|
||||
getQrCodePayload(onionBytes, setupToken.getBytes());
|
||||
private final long time = System.currentTimeMillis();
|
||||
private final MailboxProperties setupProperties = new MailboxProperties(
|
||||
onion, setupToken, new ArrayList<>());
|
||||
@@ -68,32 +82,50 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testInitialQrCodeReceivedState() {
|
||||
MailboxPairingTask task =
|
||||
factory.createPairingTask(getRandomString(42));
|
||||
MailboxPairingTask task = createPairingTask(getRandomString(42));
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.QrCodeReceived)
|
||||
);
|
||||
assertTrue(state instanceof QrCodeReceived));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCode() {
|
||||
MailboxPairingTask task1 =
|
||||
factory.createPairingTask(getRandomString(42));
|
||||
task1.run();
|
||||
task1.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
||||
);
|
||||
public void testInvalidQrCodeType() {
|
||||
String payload = getRandomString(65);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
String goodLength = "00" + getRandomString(63);
|
||||
MailboxPairingTask task2 = factory.createPairingTask(goodLength);
|
||||
task2.run();
|
||||
task2.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
||||
);
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCodeVersion() {
|
||||
String payload = getRandomString(65);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION + 1);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCodeLength() {
|
||||
String payload = getRandomString(42);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulPairing() throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -121,17 +153,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
AtomicInteger i = new AtomicInteger(0);
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.addObserver(state -> {
|
||||
if (i.get() == 0) {
|
||||
assertEquals(MailboxPairingState.QrCodeReceived.class,
|
||||
state.getClass());
|
||||
assertEquals(QrCodeReceived.class, state.getClass());
|
||||
} else if (i.get() == 1) {
|
||||
assertEquals(MailboxPairingState.Pairing.class,
|
||||
state.getClass());
|
||||
assertEquals(Pairing.class, state.getClass());
|
||||
} else if (i.get() == 2) {
|
||||
assertEquals(MailboxPairingState.Paired.class,
|
||||
state.getClass());
|
||||
assertEquals(Paired.class, state.getClass());
|
||||
} else fail("Unexpected change of state " + state.getClass());
|
||||
i.getAndIncrement();
|
||||
});
|
||||
@@ -140,24 +169,23 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testAlreadyPaired() throws Exception {
|
||||
testApiException(new MailboxApi.MailboxAlreadyPairedException(),
|
||||
MailboxPairingState.MailboxAlreadyPaired.class);
|
||||
testApiException(new MailboxAlreadyPairedException(),
|
||||
MailboxAlreadyPaired.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMailboxApiException() throws Exception {
|
||||
testApiException(new MailboxApi.ApiException(),
|
||||
MailboxPairingState.UnexpectedError.class);
|
||||
testApiException(new ApiException(), UnexpectedError.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApiIOException() throws Exception {
|
||||
testApiException(new IOException(),
|
||||
MailboxPairingState.ConnectionError.class);
|
||||
testApiException(new IOException(), ConnectionError.class);
|
||||
}
|
||||
|
||||
private void testApiException(Exception e,
|
||||
Class<? extends MailboxPairingState> s) throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -165,13 +193,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
will(throwException(e));
|
||||
}});
|
||||
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.run();
|
||||
task.addObserver(state -> assertEquals(state.getClass(), s));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbException() throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -188,20 +217,10 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
will(throwException(new DbException()));
|
||||
}});
|
||||
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.run();
|
||||
task.addObserver(state -> assertEquals(state.getClass(),
|
||||
MailboxPairingState.UnexpectedError.class));
|
||||
}
|
||||
|
||||
private String getValidPayload() {
|
||||
byte[] payloadBytes = ByteBuffer.allocate(65)
|
||||
.put((byte) 32) // 1
|
||||
.put(onionBytes) // 32
|
||||
.put(setupToken.getBytes()) // 32
|
||||
.array();
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new String(payloadBytes, Charset.forName("ISO-8859-1"));
|
||||
task.addObserver(state ->
|
||||
assertEquals(state.getClass(), UnexpectedError.class));
|
||||
}
|
||||
|
||||
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
|
||||
@@ -212,4 +231,22 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
p1.getServerSupports().equals(p2.getServerSupports()));
|
||||
}
|
||||
|
||||
private MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(time));
|
||||
}});
|
||||
|
||||
return new MailboxPairingTaskImpl(qrCodePayload, executor, db,
|
||||
crypto, clock, api, mailboxSettingsManager,
|
||||
mailboxUpdateManager, qrCodeClassifier);
|
||||
}
|
||||
|
||||
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||
int formatVersion) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_ID;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
class MailboxTestUtils {
|
||||
|
||||
static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) {
|
||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||
byte[] payloadBytes = ByteBuffer.allocate(65)
|
||||
.put((byte) formatIdAndVersion) // 1
|
||||
.put(onionBytes) // 32
|
||||
.put(setupToken) // 32
|
||||
.array();
|
||||
return new String(payloadBytes, ISO_8859_1);
|
||||
}
|
||||
|
||||
// Used by mailbox integration tests
|
||||
static String getQrCodePayload(byte[] setupToken) {
|
||||
return getQrCodePayload(getRandomId(), setupToken);
|
||||
}
|
||||
|
||||
static WeakSingletonProvider<OkHttpClient> createHttpClientProvider() {
|
||||
return new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public OkHttpClient createInstance() {
|
||||
return new OkHttpClient.Builder()
|
||||
.socketFactory(SocketFactory.getDefault())
|
||||
.connectTimeout(60_000, MILLISECONDS)
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public class TorReachabilityMonitorImplTest extends BrambleMockTestCase {
|
||||
private final Executor ioExecutor = context.mock(Executor.class);
|
||||
private final TaskScheduler taskScheduler =
|
||||
context.mock(TaskScheduler.class);
|
||||
private final MailboxConfig mailboxConfig = new MailboxConfigImpl();
|
||||
private final PluginManager pluginManager =
|
||||
context.mock(PluginManager.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
@@ -39,7 +40,7 @@ public class TorReachabilityMonitorImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final TorReachabilityMonitorImpl monitor =
|
||||
new TorReachabilityMonitorImpl(ioExecutor, taskScheduler,
|
||||
pluginManager, eventBus);
|
||||
mailboxConfig, pluginManager, eventBus);
|
||||
|
||||
@Test
|
||||
public void testSchedulesTaskWhenStartedIfTorIsActive() {
|
||||
|
||||
@@ -9,7 +9,8 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.plugin.file.RemovableDriveManager;
|
||||
import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
|
||||
import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
||||
import org.briarproject.bramble.mailbox.UrlConverterModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.TimeTravelModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||
@@ -29,13 +30,14 @@ import dagger.Component;
|
||||
DefaultBatteryManagerModule.class,
|
||||
DefaultEventExecutorModule.class,
|
||||
DefaultWakefulIoExecutorModule.class,
|
||||
DefaultThreadFactoryModule.class,
|
||||
TestDatabaseConfigModule.class,
|
||||
TestDnsModule.class,
|
||||
TestFeatureFlagModule.class,
|
||||
TestMailboxDirectoryModule.class,
|
||||
RemovableDriveIntegrationTestModule.class,
|
||||
RemovableDriveModule.class,
|
||||
UrlConverterModule.class,
|
||||
ModularMailboxModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
TimeTravelModule.class,
|
||||
TestSocksModule.class,
|
||||
|
||||
@@ -217,13 +217,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
||||
// The plugin should have bound a socket and stored the port number
|
||||
BdfList descriptor = kal.getDescriptor();
|
||||
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);
|
||||
InetAddress addr = InetAddress.getByAddress(address);
|
||||
assertTrue(addr instanceof Inet4Address);
|
||||
assertFalse(addr.isLoopbackAddress());
|
||||
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
|
||||
int port = descriptor.getLong(2).intValue();
|
||||
int port = descriptor.getInt(2);
|
||||
assertTrue(port > 0 && port < 65536);
|
||||
// The plugin should be listening on the port
|
||||
InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
|
||||
|
||||
@@ -404,7 +404,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -471,7 +471,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup2.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -526,7 +526,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||
will(returnValue(update));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -564,7 +564,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||
will(returnValue(update));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -695,7 +695,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(localGroupMessageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||
false);
|
||||
will(returnValue(oldUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
oldPropertiesDict);
|
||||
@@ -760,7 +761,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(localGroupMessageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||
false);
|
||||
will(returnValue(oldUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
oldPropertiesDict);
|
||||
@@ -819,12 +821,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
// Retrieve and parse the latest local properties
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
will(returnValue(fooProperties));
|
||||
oneOf(clientHelper).getMessageAsList(txn, barVersion3);
|
||||
oneOf(clientHelper).getMessageAsList(txn, barVersion3, false);
|
||||
will(returnValue(barUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
barPropertiesDict);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class QrCodeClassifierImplTest extends BrambleTestCase {
|
||||
|
||||
private final QrCodeClassifier classifier = new QrCodeClassifierImpl();
|
||||
|
||||
@Test
|
||||
public void testClassifiesEmptyStringAsUnknown() {
|
||||
Pair<QrCodeType, Integer> result = classifier.classifyQrCode("");
|
||||
assertEquals(UNKNOWN, result.getFirst());
|
||||
assertEquals(0, result.getSecond().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesKeyAgreement() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
for (int version = 0; version < 32; version++) {
|
||||
int typeAndVersion =
|
||||
(KeyAgreementConstants.QR_FORMAT_ID << 5) | version;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result =
|
||||
classifier.classifyQrCode(payload);
|
||||
assertEquals(BQP, result.getFirst());
|
||||
assertEquals(version, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesMailbox() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
for (int version = 0; version < 32; version++) {
|
||||
int typeAndVersion =
|
||||
(MailboxConstants.QR_FORMAT_ID << 5) | version;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result =
|
||||
classifier.classifyQrCode(payload);
|
||||
assertEquals(MAILBOX, result.getFirst());
|
||||
assertEquals(version, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesUnknownFormatIdAsUnknown() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
int unknownFormatId = MailboxConstants.QR_FORMAT_ID + 1;
|
||||
int typeAndVersion = unknownFormatId << 5;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result = classifier.classifyQrCode(payload);
|
||||
assertEquals(UNKNOWN, result.getFirst());
|
||||
assertEquals(0, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.mailbox.UrlConverterModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
@@ -16,7 +16,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class,
|
||||
UrlConverterModule.class,
|
||||
ModularMailboxModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
|
||||
@@ -6,13 +6,18 @@ import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
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.MessageId;
|
||||
import org.briarproject.bramble.api.sync.Offer;
|
||||
import org.briarproject.bramble.api.sync.Priority;
|
||||
import org.briarproject.bramble.api.sync.Request;
|
||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||
import org.briarproject.bramble.api.sync.Versions;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.PredicateMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jmock.Expectations;
|
||||
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.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.PRIORITY;
|
||||
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.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_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.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
@@ -46,6 +54,38 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
private final SyncRecordReader reader =
|
||||
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
|
||||
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
||||
expectReadRecord(createAck());
|
||||
@@ -158,6 +198,20 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
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 {
|
||||
context.checking(new Expectations() {{
|
||||
//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 {
|
||||
return new Record(PROTOCOL_VERSION, ACK, createPayload());
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import dagger.Module;
|
||||
DefaultBatteryManagerModule.class,
|
||||
DefaultEventExecutorModule.class,
|
||||
DefaultWakefulIoExecutorModule.class,
|
||||
TestThreadFactoryModule.class,
|
||||
TestDatabaseConfigModule.class,
|
||||
TestFeatureFlagModule.class,
|
||||
TestMailboxDirectoryModule.class,
|
||||
|
||||
@@ -235,11 +235,16 @@ public abstract class BrambleIntegrationTest<C extends BrambleIntegrationTestCom
|
||||
|
||||
protected void awaitPendingMessageDelivery(int num)
|
||||
throws TimeoutException {
|
||||
deliveryWaiter.await(TIMEOUT, num);
|
||||
awaitPendingMessageDelivery(num, TIMEOUT);
|
||||
}
|
||||
|
||||
protected void awaitPendingMessageDelivery(int num, long timeout)
|
||||
throws TimeoutException {
|
||||
deliveryWaiter.await(timeout, num);
|
||||
assertEquals("Messages delivered", num, deliveryCounter.getAndSet(0));
|
||||
|
||||
try {
|
||||
messageSemaphore.tryAcquire(num, TIMEOUT, MILLISECONDS);
|
||||
messageSemaphore.tryAcquire(num, timeout, MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.info("Interrupted while waiting for messages");
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.mailbox.UrlConverterModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -16,7 +16,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class,
|
||||
UrlConverterModule.class,
|
||||
ModularMailboxModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class TestThreadFactoryModule {
|
||||
|
||||
@Nullable
|
||||
private final String prefix;
|
||||
|
||||
public TestThreadFactoryModule() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public TestThreadFactoryModule(@Nullable String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Provides
|
||||
ThreadFactory provideThreadFactory() {
|
||||
if (prefix == null) return Executors.defaultThreadFactory();
|
||||
return new TestThreadFactory(prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is mostly copied from
|
||||
* {@link Executors#defaultThreadFactory()} only adds a given prefix.
|
||||
*/
|
||||
static class TestThreadFactory implements ThreadFactory {
|
||||
private static final AtomicInteger poolNumber = new AtomicInteger(1);
|
||||
private final ThreadGroup group;
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
private final String namePrefix;
|
||||
|
||||
private TestThreadFactory(String prefix) {
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
this.group = s != null ? s.getThreadGroup() :
|
||||
Thread.currentThread().getThreadGroup();
|
||||
this.namePrefix =
|
||||
prefix + "-p-" + poolNumber.getAndIncrement() + "-t-";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(@Nonnull Runnable r) {
|
||||
Thread t = new Thread(this.group, r,
|
||||
this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
|
||||
if (t.isDaemon()) {
|
||||
t.setDaemon(false);
|
||||
}
|
||||
|
||||
if (t.getPriority() != 5) {
|
||||
t.setPriority(5);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.transport.KeyManager;
|
||||
import org.briarproject.bramble.mailbox.UrlConverterModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.BrambleIntegrationTestComponent;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
@@ -22,7 +22,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class,
|
||||
UrlConverterModule.class,
|
||||
ModularMailboxModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
|
||||
@@ -88,51 +88,58 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8() {
|
||||
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8()
|
||||
throws Exception {
|
||||
// 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
|
||||
public void testFromUtf8RemovesNullCharacterUsingModifiedUtf8() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromUtf8RejectsNullCharacterUsingModifiedUtf8()
|
||||
throws Exception {
|
||||
// The modified UTF-8 encoding of the null character is not valid
|
||||
byte[] b = new byte[] {
|
||||
(byte) 0xC0, (byte) 0x80, // Null character as modified UTF-8
|
||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
// Conversion should ignore the invalid character and return the rest
|
||||
String expected = "\u0205";
|
||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
||||
StringUtils.fromUtf8(b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8() {
|
||||
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8()
|
||||
throws Exception {
|
||||
// The UTF-8 encoding of a supplementary character is valid and should
|
||||
// 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) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
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
|
||||
public void testFromUtf8RemovesSupplementaryCharacterUsingModifiedUtf8() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromUtf8RejectsSupplementaryCharacterUsingModifiedUtf8()
|
||||
throws Exception {
|
||||
// The CESU-8 or modified UTF-8 encoding of a supplementary character
|
||||
// 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) 0xB0, (byte) 0x80,
|
||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
// Conversion should ignore the invalid character and return the rest
|
||||
String expected = "\u0205";
|
||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
||||
StringUtils.fromUtf8(utf8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8EmptyInput() {
|
||||
public void testFromUtf8EmptyInput() throws Exception {
|
||||
assertEquals("", StringUtils.fromUtf8(new byte[0]));
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user