mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
2 Commits
beta-1.4.9
...
checkstyle
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a13adcffb | ||
|
|
f4d885b647 |
@@ -32,9 +32,8 @@ test:
|
|||||||
extends: .base-test
|
extends: .base-test
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- ./gradlew -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
|
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
|
||||||
- ./gradlew -Djava.security.egd=file:/dev/urandom assembleOfficialDebug :briar-headless:linuxJars
|
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
|
||||||
- ./gradlew -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
|
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
when: always
|
when: always
|
||||||
@@ -62,7 +61,7 @@ android test:
|
|||||||
when: on_failure
|
when: on_failure
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
when: manual
|
when: on_success
|
||||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
changes:
|
changes:
|
||||||
- briar-android/**/*
|
- briar-android/**/*
|
||||||
@@ -85,36 +84,35 @@ test_reproducible:
|
|||||||
|
|
||||||
.optional_tests:
|
.optional_tests:
|
||||||
stage: optional_tests
|
stage: optional_tests
|
||||||
extends: .base-test
|
before_script:
|
||||||
|
- set -e
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: "$CI_COMMIT_REF_SLUG"
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
|
script:
|
||||||
|
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
# these file change every time but should not be cached
|
||||||
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
|
|
||||||
bridge test:
|
bridge test:
|
||||||
extends: .optional_tests
|
extends: .optional_tests
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
when: on_success
|
when: on_success
|
||||||
allow_failure: false
|
allow_failure: true
|
||||||
- if: '$CI_COMMIT_TAG == null'
|
- if: '$CI_COMMIT_TAG == null'
|
||||||
when: manual
|
when: manual
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
script:
|
|
||||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
|
||||||
timeout: 3h
|
|
||||||
|
|
||||||
mailbox integration test:
|
pre_release_tests:
|
||||||
extends: .optional_tests
|
extends: .optional_tests
|
||||||
rules:
|
only:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
- tags
|
||||||
when: on_success
|
|
||||||
- if: '$CI_COMMIT_TAG == null'
|
|
||||||
when: manual
|
|
||||||
allow_failure: true # TODO figure out how not to allow failure while leaving this optional
|
|
||||||
script:
|
|
||||||
# start mailbox
|
|
||||||
- cd /opt && git clone --depth 1 https://code.briarproject.org/briar/briar-mailbox.git briar-mailbox
|
|
||||||
- cd briar-mailbox
|
|
||||||
- mkdir -p /root/.local/share # create directory that mailbox (currently) expects to exist
|
|
||||||
- ./gradlew run --args="--debug --setup-token 54686973206973206120736574757020746f6b656e20666f722042726961722e" &
|
|
||||||
# run mailbox integration test once mailbox has started
|
|
||||||
- cd "$CI_PROJECT_DIR"
|
|
||||||
- bramble-core/src/test/bash/wait-for-mailbox.sh
|
|
||||||
- OPTIONAL_TESTS=org.briarproject.bramble.mailbox.MailboxIntegrationTest ./gradlew --info bramble-core:test --tests MailboxIntegrationTest
|
|
||||||
|
|||||||
@@ -22,15 +22,6 @@ our site.
|
|||||||
|
|
||||||
[Wiki](https://code.briarproject.org/briar/briar/-/wikis/home)
|
[Wiki](https://code.briarproject.org/briar/briar/-/wikis/home)
|
||||||
|
|
||||||
## Reproducible builds
|
|
||||||
|
|
||||||
We provide [docker images](https://code.briarproject.org/briar/briar-reproducer#briar-reproducer)
|
|
||||||
to ease the task of verifying that the published APK binaries
|
|
||||||
include nothing but our publicly available source code.
|
|
||||||
|
|
||||||
You can either use those images or use them as a blueprint to build your own environment
|
|
||||||
for reproduction.
|
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
|
|
||||||
[](https://liberapay.com/Briar/donate) [](https://flattr.com/t/592836/)
|
[](https://liberapay.com/Briar/donate) [](https://flattr.com/t/592836/)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import com.android.build.gradle.tasks.MergeResources
|
import com.android.build.gradle.tasks.MergeResources
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 10409
|
versionCode 10401
|
||||||
versionName "1.4.9"
|
versionName "1.4.1"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -42,8 +43,8 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
tor "org.briarproject:tor-android:$tor_version"
|
tor 'org.briarproject:tor-android:0.3.5.15'
|
||||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
|
||||||
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ dependencies {
|
|||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
testImplementation "org.jmock:jmock-legacy:$jmock_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
def torBinariesDir = 'src/main/res/raw'
|
def torBinariesDir = 'src/main/res/raw'
|
||||||
@@ -70,6 +71,11 @@ clean.dependsOn cleanTorBinaries
|
|||||||
|
|
||||||
task unpackTorBinaries {
|
task unpackTorBinaries {
|
||||||
doLast {
|
doLast {
|
||||||
|
copy {
|
||||||
|
from configurations.tor.collect { zipTree(it) }
|
||||||
|
into torBinariesDir
|
||||||
|
include 'geoip.zip'
|
||||||
|
}
|
||||||
configurations.tor.each { outer ->
|
configurations.tor.each { outer ->
|
||||||
zipTree(outer).each { inner ->
|
zipTree(outer).each { inner ->
|
||||||
if (inner.name.endsWith('_arm_pie.zip')) {
|
if (inner.name.endsWith('_arm_pie.zip')) {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# Keep the H2 classes that are loaded via reflection
|
-keep,includedescriptorclasses class org.briarproject.** { *; }
|
||||||
-keep class org.h2.Driver { *; }
|
|
||||||
-keep class org.h2.engine.Engine { *; }
|
-keep class org.h2.** { *; }
|
||||||
-keep class org.h2.store.fs.** { *; }
|
|
||||||
# Don't warn about unused dependencies of H2 classes
|
|
||||||
-dontwarn org.h2.**
|
-dontwarn org.h2.**
|
||||||
-dontnote org.h2.**
|
-dontnote org.h2.**
|
||||||
|
|
||||||
@@ -17,4 +15,5 @@
|
|||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-dontnote com.google.common.**
|
-dontnote com.google.common.**
|
||||||
|
|
||||||
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
|
# UPnP library isn't used
|
||||||
|
-dontwarn org.bitlet.weupnp.**
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ import android.net.LinkAddress;
|
|||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.wifi.WifiInfo;
|
|
||||||
import android.net.wifi.WifiManager;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventExecutor;
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
@@ -24,6 +21,7 @@ import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
|
||||||
|
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -40,7 +38,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
|
||||||
import static android.content.Intent.ACTION_SCREEN_OFF;
|
import static android.content.Intent.ACTION_SCREEN_OFF;
|
||||||
import static android.content.Intent.ACTION_SCREEN_ON;
|
import static android.content.Intent.ACTION_SCREEN_ON;
|
||||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||||
@@ -114,37 +111,15 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetworkStatus getNetworkStatus() {
|
public NetworkStatus getNetworkStatus() {
|
||||||
// https://issuetracker.google.com/issues/175055271
|
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||||
try {
|
boolean connected = net != null && net.isConnected();
|
||||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
boolean wifi = false, ipv6Only = false;
|
||||||
boolean connected = net != null && net.isConnected();
|
if (connected) {
|
||||||
boolean wifi = false, ipv6Only = false;
|
wifi = net.getType() == TYPE_WIFI;
|
||||||
if (connected) {
|
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
|
||||||
wifi = net.getType() == TYPE_WIFI;
|
else ipv6Only = areAllAvailableNetworksIpv6Only();
|
||||||
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
|
|
||||||
else ipv6Only = areAllAvailableNetworksIpv6Only();
|
|
||||||
}
|
|
||||||
return new NetworkStatus(connected, wifi, ipv6Only);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
// Without the ConnectivityManager we can't detect whether we have
|
|
||||||
// internet access. Assume we do, which is probably less harmful
|
|
||||||
// than assuming we don't. Likewise, assume the connection is
|
|
||||||
// IPv6-only. Fall back to the WifiManager to detect whether we
|
|
||||||
// have a wifi connection.
|
|
||||||
LOG.info("ConnectivityManager is broken, guessing connectivity");
|
|
||||||
boolean connected = true, wifi = false, ipv6Only = true;
|
|
||||||
WifiManager wm = (WifiManager) app.getSystemService(WIFI_SERVICE);
|
|
||||||
if (wm != null) {
|
|
||||||
WifiInfo info = wm.getConnectionInfo();
|
|
||||||
if (info != null && info.getIpAddress() != 0) {
|
|
||||||
LOG.info("Connected to wifi");
|
|
||||||
wifi = true;
|
|
||||||
ipv6Only = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new NetworkStatus(connected, wifi, ipv6Only);
|
|
||||||
}
|
}
|
||||||
|
return new NetworkStatus(connected, wifi, ipv6Only);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,29 +130,23 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
*/
|
*/
|
||||||
@TargetApi(23)
|
@TargetApi(23)
|
||||||
private boolean isActiveNetworkIpv6Only() {
|
private boolean isActiveNetworkIpv6Only() {
|
||||||
// https://issuetracker.google.com/issues/175055271
|
Network net = connectivityManager.getActiveNetwork();
|
||||||
try {
|
if (net == null) {
|
||||||
Network net = connectivityManager.getActiveNetwork();
|
LOG.info("No active network");
|
||||||
if (net == null) {
|
|
||||||
LOG.info("No active network");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LinkProperties props = connectivityManager.getLinkProperties(net);
|
|
||||||
if (props == null) {
|
|
||||||
LOG.info("No link properties for active network");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean hasIpv6Unicast = false;
|
|
||||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
|
||||||
InetAddress addr = linkAddress.getAddress();
|
|
||||||
if (addr instanceof Inet4Address) return false;
|
|
||||||
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
|
||||||
}
|
|
||||||
return hasIpv6Unicast;
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||||
|
if (props == null) {
|
||||||
|
LOG.info("No link properties for active network");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasIpv6Unicast = false;
|
||||||
|
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||||
|
InetAddress addr = linkAddress.getAddress();
|
||||||
|
if (addr instanceof Inet4Address) return false;
|
||||||
|
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
||||||
|
}
|
||||||
|
return hasIpv6Unicast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,22 +32,13 @@ class AndroidRemovableDrivePlugin extends RemovableDrivePlugin {
|
|||||||
InputStream openInputStream(TransportProperties p) throws IOException {
|
InputStream openInputStream(TransportProperties p) throws IOException {
|
||||||
String uri = p.get(PROP_URI);
|
String uri = p.get(PROP_URI);
|
||||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||||
try {
|
return app.getContentResolver().openInputStream(Uri.parse(uri));
|
||||||
return app.getContentResolver().openInputStream(Uri.parse(uri));
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
OutputStream openOutputStream(TransportProperties p) throws IOException {
|
OutputStream openOutputStream(TransportProperties p) throws IOException {
|
||||||
String uri = p.get(PROP_URI);
|
String uri = p.get(PROP_URI);
|
||||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||||
try {
|
return app.getContentResolver().openOutputStream(Uri.parse(uri));
|
||||||
return app.getContentResolver()
|
|
||||||
.openOutputStream(Uri.parse(uri), "wt");
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,24 +175,16 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
@Nullable
|
@Nullable
|
||||||
private InetAddress getWifiClientIpv6Address() {
|
private InetAddress getWifiClientIpv6Address() {
|
||||||
// https://issuetracker.google.com/issues/175055271
|
for (Network net : connectivityManager.getAllNetworks()) {
|
||||||
try {
|
NetworkCapabilities caps =
|
||||||
for (Network net : connectivityManager.getAllNetworks()) {
|
connectivityManager.getNetworkCapabilities(net);
|
||||||
NetworkCapabilities caps =
|
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue;
|
||||||
connectivityManager.getNetworkCapabilities(net);
|
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||||
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) {
|
if (props == null) continue;
|
||||||
continue;
|
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||||
}
|
InetAddress addr = linkAddress.getAddress();
|
||||||
LinkProperties props =
|
if (isIpv6LinkLocalAddress(addr)) return addr;
|
||||||
connectivityManager.getLinkProperties(net);
|
|
||||||
if (props == null) continue;
|
|
||||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
|
||||||
InetAddress addr = linkAddress.getAddress();
|
|
||||||
if (isIpv6LinkLocalAddress(addr)) return addr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (SecurityException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -235,17 +227,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
// network's socket factory may try to connect via another network
|
// network's socket factory may try to connect via another network
|
||||||
private SocketFactory getSocketFactory() {
|
private SocketFactory getSocketFactory() {
|
||||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
if (SDK_INT < 21) return SocketFactory.getDefault();
|
||||||
// https://issuetracker.google.com/issues/175055271
|
for (Network net : connectivityManager.getAllNetworks()) {
|
||||||
try {
|
NetworkCapabilities caps =
|
||||||
for (Network net : connectivityManager.getAllNetworks()) {
|
connectivityManager.getNetworkCapabilities(net);
|
||||||
NetworkCapabilities caps =
|
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
||||||
connectivityManager.getNetworkCapabilities(net);
|
return net.getSocketFactory();
|
||||||
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
|
||||||
return net.getSocketFactory();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (SecurityException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
}
|
||||||
LOG.warning("Could not find suitable socket factory");
|
LOG.warning("Could not find suitable socket factory");
|
||||||
return SocketFactory.getDefault();
|
return SocketFactory.getDefault();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
@@ -11,35 +10,61 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedArchitectures;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class AndroidTorPluginFactory extends TorPluginFactory {
|
public class AndroidTorPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(AndroidTorPluginFactory.class.getName());
|
||||||
|
|
||||||
|
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
|
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
||||||
|
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
||||||
|
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||||
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
|
|
||||||
|
private final Executor ioExecutor, wakefulIoExecutor;
|
||||||
private final Application app;
|
private final Application app;
|
||||||
|
private final NetworkManager networkManager;
|
||||||
|
private final LocationUtils locationUtils;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final SocketFactory torSocketFactory;
|
||||||
|
private final BackoffFactory backoffFactory;
|
||||||
|
private final ResourceProvider resourceProvider;
|
||||||
|
private final CircumventionProvider circumventionProvider;
|
||||||
|
private final BatteryManager batteryManager;
|
||||||
private final AndroidWakeLockManager wakeLockManager;
|
private final AndroidWakeLockManager wakeLockManager;
|
||||||
|
private final Clock clock;
|
||||||
|
private final File torDirectory;
|
||||||
|
private int torSocksPort;
|
||||||
|
private int torControlPort;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
|
Application app,
|
||||||
NetworkManager networkManager,
|
NetworkManager networkManager,
|
||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
@@ -48,43 +73,77 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
|
AndroidWakeLockManager wakeLockManager,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
CryptoComponent crypto,
|
|
||||||
@TorDirectory File torDirectory,
|
@TorDirectory File torDirectory,
|
||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort,
|
@TorControlPort int torControlPort) {
|
||||||
Application app,
|
this.ioExecutor = ioExecutor;
|
||||||
AndroidWakeLockManager wakeLockManager) {
|
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
|
||||||
torDirectory, torSocksPort, torControlPort);
|
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
this.networkManager = networkManager;
|
||||||
|
this.locationUtils = locationUtils;
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.torSocketFactory = torSocketFactory;
|
||||||
|
this.backoffFactory = backoffFactory;
|
||||||
|
this.resourceProvider = resourceProvider;
|
||||||
|
this.circumventionProvider = circumventionProvider;
|
||||||
|
this.batteryManager = batteryManager;
|
||||||
this.wakeLockManager = wakeLockManager;
|
this.wakeLockManager = wakeLockManager;
|
||||||
|
this.clock = clock;
|
||||||
|
this.torDirectory = torDirectory;
|
||||||
|
this.torSocksPort = torSocksPort;
|
||||||
|
this.torControlPort = torControlPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
String getArchitectureForTorBinary() {
|
public TransportId getId() {
|
||||||
for (String abi : getSupportedArchitectures()) {
|
return TorConstants.ID;
|
||||||
if (abi.startsWith("x86_64")) return "x86_64_pie";
|
}
|
||||||
else if (abi.startsWith("x86")) return "x86_pie";
|
|
||||||
else if (abi.startsWith("arm64")) return "arm64_pie";
|
@Override
|
||||||
else if (abi.startsWith("armeabi")) return "arm_pie";
|
public long getMaxLatency() {
|
||||||
|
return MAX_LATENCY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
|
|
||||||
|
// Check that we have a Tor binary for this architecture
|
||||||
|
String architecture = null;
|
||||||
|
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
||||||
|
if (abi.startsWith("x86_64")) {
|
||||||
|
architecture = "x86_64";
|
||||||
|
break;
|
||||||
|
} else if (abi.startsWith("x86")) {
|
||||||
|
architecture = "x86";
|
||||||
|
break;
|
||||||
|
} else if (abi.startsWith("arm64")) {
|
||||||
|
architecture = "arm64";
|
||||||
|
break;
|
||||||
|
} else if (abi.startsWith("armeabi")) {
|
||||||
|
architecture = "arm";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
if (architecture == null) {
|
||||||
}
|
LOG.info("Tor is not supported on this architecture");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Use position-independent executable
|
||||||
|
architecture += "_pie";
|
||||||
|
|
||||||
@Override
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
TorPlugin createPluginInstance(Backoff backoff,
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
|
||||||
String architecture) {
|
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
|
||||||
return new AndroidTorPlugin(ioExecutor,
|
|
||||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
wakefulIoExecutor, app, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, wakeLockManager,
|
circumventionProvider, batteryManager, wakeLockManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
backoff, torRendezvousCrypto, callback, architecture,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
|
eventBus.addListener(plugin);
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import android.content.Intent;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.AlarmListener;
|
import org.briarproject.bramble.api.system.AlarmListener;
|
||||||
@@ -117,12 +116,10 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
|||||||
long dueMillis = now + MILLISECONDS.convert(delay, unit);
|
long dueMillis = now + MILLISECONDS.convert(delay, unit);
|
||||||
Runnable wakeful = () ->
|
Runnable wakeful = () ->
|
||||||
wakeLockManager.executeWakefully(task, executor, "TaskHandoff");
|
wakeLockManager.executeWakefully(task, executor, "TaskHandoff");
|
||||||
// Acquire the lock before scheduling the check to ensure the check
|
Future<?> check = scheduleCheckForDueTasks(delay, unit);
|
||||||
// doesn't access the task queue before the task has been added
|
ScheduledTask s = new ScheduledTask(wakeful, dueMillis, check,
|
||||||
ScheduledTask s;
|
cancelled);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
Future<?> check = scheduleCheckForDueTasks(delay, unit);
|
|
||||||
s = new ScheduledTask(wakeful, dueMillis, check, cancelled);
|
|
||||||
tasks.add(s);
|
tasks.add(s);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
@@ -139,7 +136,6 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
|||||||
return schedule(wrapped, executor, delay, unit, cancelled);
|
return schedule(wrapped, executor, delay, unit, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private Future<?> scheduleCheckForDueTasks(long delay, TimeUnit unit) {
|
private Future<?> scheduleCheckForDueTasks(long delay, TimeUnit unit) {
|
||||||
Runnable wakeful = () -> wakeLockManager.runWakefully(
|
Runnable wakeful = () -> wakeLockManager.runWakefully(
|
||||||
this::runDueTasks, "TaskScheduler");
|
this::runDueTasks, "TaskScheduler");
|
||||||
@@ -210,7 +206,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
|||||||
private final Future<?> check;
|
private final Future<?> check;
|
||||||
private final AtomicBoolean cancelled;
|
private final AtomicBoolean cancelled;
|
||||||
|
|
||||||
private ScheduledTask(Runnable task, long dueMillis,
|
public ScheduledTask(Runnable task, long dueMillis,
|
||||||
Future<?> check, AtomicBoolean cancelled) {
|
Future<?> check, AtomicBoolean cancelled) {
|
||||||
this.task = task;
|
this.task = task;
|
||||||
this.dueMillis = dueMillis;
|
this.dueMillis = dueMillis;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
@@ -135,8 +134,4 @@ public class AndroidUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUiThread() {
|
|
||||||
return Looper.myLooper() == Looper.getMainLooper();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
|||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.imposters.ByteBuddyClassImposteriser;
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -44,7 +44,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
private AndroidAccountManager accountManager;
|
private AndroidAccountManager accountManager;
|
||||||
|
|
||||||
public AndroidAccountManagerTest() {
|
public AndroidAccountManagerTest() {
|
||||||
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
app = context.mock(Application.class);
|
app = context.mock(Application.class);
|
||||||
applicationInfo = new ApplicationInfo();
|
applicationInfo = new ApplicationInfo();
|
||||||
applicationInfo.dataDir = testDir.getAbsolutePath();
|
applicationInfo.dataDir = testDir.getAbsolutePath();
|
||||||
|
|||||||
@@ -1,161 +1,123 @@
|
|||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
|
'antlr:antlr:2.7.7:antlr-2.7.7.jar:88fbda4b912596b9f56e8e12e580cc954bacfb51776ecfddd3e18fc1cf56dc4c',
|
||||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
'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:protos:27.1.3:protos-27.1.3.jar:0d9e6cff60b318baac250b6f5bb076a8161103338bf2749cdf1db8a5a13a1f12',
|
||||||
'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15',
|
'com.android.tools.analytics-library:shared:27.1.3:shared-27.1.3.jar:10d2a51d8f89ff4ac849888e5a9c60b10e879c30d78545ec1da4d3df7bd56ae4',
|
||||||
'com.android.tools.analytics-library:tracker:30.0.3:tracker-30.0.3.jar:5d0ef35bf6733e96210b5085a2a202152921bf834d345959dce1ca3369b528df',
|
'com.android.tools.analytics-library:tracker:27.1.3:tracker-27.1.3.jar:589b355a2ba796cbc0a2b2295737de6661f078262e5f87cd6f540b8d011e5ebb',
|
||||||
'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca',
|
'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:apksig:4.1.3:apksig-4.1.3.jar:a851980c678ff7a6785388b9a9e8cc094788ce3c4a985ad2b19c2028fd3c631a',
|
||||||
'com.android.tools.build:apkzlib:7.0.3:apkzlib-7.0.3.jar:b31e53174c92db83c5cc6e7dac6734ea4e907a72e452c2bf1818dfd082c59397',
|
'com.android.tools.build:apkzlib:4.1.3:apkzlib-4.1.3.jar:475903065e7e83a8c1ba78d267c97a54dc5a04d768b535093850423d7b11f2c8',
|
||||||
'com.android.tools.build:builder-model:7.0.3:builder-model-7.0.3.jar:483f99d7494a5bed027e1e8d29111384cf535d4842f0be5a79805bd44bb68d4e',
|
'com.android.tools.build:builder-model:4.1.3:builder-model-4.1.3.jar:2624a1436c3ab39dd91d3ecf9409a594b0f89ea5cab255f2e9ff11f5ee03d274',
|
||||||
'com.android.tools.build:builder-test-api:7.0.3:builder-test-api-7.0.3.jar:f6de4bc2cef545e8367bf82d7c733829c7be3b0b3b8b09fd8c58f2150e59ab46',
|
'com.android.tools.build:builder-test-api:4.1.3:builder-test-api-4.1.3.jar:3d2af66726b06b53b8d6d497efcee39ff9f77eb2f8d2cce38b31502383a40d2c',
|
||||||
'com.android.tools.build:builder:7.0.3:builder-7.0.3.jar:c6952da0094b094c2ba0fe84c675622097c5d9b9f9beb53485b860320540cf1d',
|
'com.android.tools.build:builder:4.1.3:builder-4.1.3.jar:a40426cd6d68f6a722ef4950058c075e4547025e8c2fd78e732ad89f15176f84',
|
||||||
'com.android.tools.build:manifest-merger:30.0.3:manifest-merger-30.0.3.jar:72b346ba6318b4b6260e6e49df4bea5da2e12329ab6c2beb2269c49a9f51f178',
|
'com.android.tools.build:gradle-api:4.1.3:gradle-api-4.1.3.jar:11b1fb9de658bdcf9290b1c1517060d0c4d93f2b27975934989ca4ac890bc077',
|
||||||
'com.android.tools.ddms:ddmlib:30.0.3:ddmlib-30.0.3.jar:7a914a68ab93393657297234e2f37b22410ae9a433cba692ce8c727c9607e3bb',
|
'com.android.tools.build:manifest-merger:27.1.3:manifest-merger-27.1.3.jar:ce8d4009b1f1584777a7ffa1da3b0551dc316bc8e08112e442c352af70f46f2d',
|
||||||
'com.android.tools.external.com-intellij:intellij-core:30.0.3:intellij-core-30.0.3.jar:1ebe858d3f58eeaa8c06507f8ac0f1c7051e6c61f35a70f3c3967d5734d3abc5',
|
'com.android.tools.ddms:ddmlib:27.1.3:ddmlib-27.1.3.jar:8f76e8236d2b9eebf26378746dad025c4c7c056a02e133dae4ddef47b283c710',
|
||||||
'com.android.tools.external.com-intellij:kotlin-compiler:30.0.3:kotlin-compiler-30.0.3.jar:ed00e441f427cb4e0d418287b9da30b12b7f735f9af32e6b5d3dc960b6a742fc',
|
'com.android.tools.external.com-intellij:intellij-core:27.1.3:intellij-core-27.1.3.jar:652814fa099b4746fb6f10e19718e476952e8b5bac24e17d914f90650ad21808',
|
||||||
'com.android.tools.external.org-jetbrains:uast:30.0.3:uast-30.0.3.jar:a77801bee6ff509910e459525c9c34d7f04b066ade123547f16f1917548eadea',
|
'com.android.tools.external.com-intellij:kotlin-compiler:27.1.3:kotlin-compiler-27.1.3.jar:8d7a78d5efd213c5e467e42bd205582aad73ffc77ee5dc18eb1361c9af72f125',
|
||||||
'com.android.tools.layoutlib:layoutlib-api:30.0.3:layoutlib-api-30.0.3.jar:4caa87e9ca2e11315f650d576cd59fec1793373bc3fca3f6d53c029e7534e7c4',
|
'com.android.tools.external.org-jetbrains:uast:27.1.3:uast-27.1.3.jar:aea53944a1ac6a05f12297b55290e8cbecfe54c4166260cfba4405823bfe1c78',
|
||||||
'com.android.tools.lint:lint-api:30.0.3:lint-api-30.0.3.jar:bcecbd2f752a6560096a9029a47d1de6bd788a51bab505c5ebfba6a18524b983',
|
'com.android.tools.layoutlib:layoutlib-api:27.1.3:layoutlib-api-27.1.3.jar:23875ce0a8429f33a4e86cc358f658faa0ba9c576f5f05760e544b453d67d04b',
|
||||||
'com.android.tools.lint:lint-checks:30.0.3:lint-checks-30.0.3.jar:25a7cd42dc3ad502337f131fb8b7e873c53301db0a67b1c64dd4ae7a8eb66cec',
|
'com.android.tools.lint:lint-api:27.1.3:lint-api-27.1.3.jar:97666be32bcadacd944416ea334a9575ef8f4ad0c8f333151491ff4a7df43e1c',
|
||||||
'com.android.tools.lint:lint-gradle:30.0.3:lint-gradle-30.0.3.jar:94544d6147a809bf2fd3440e51f28a4e42e547d74aab53eefd74938cdad42c26',
|
'com.android.tools.lint:lint-checks:27.1.3:lint-checks-27.1.3.jar:b2d71ae84a31490fe9ff26c706163fe245b2aea98e3eb747214c1085df444708',
|
||||||
'com.android.tools.lint:lint-model:30.0.3:lint-model-30.0.3.jar:0b940a7f575c2ff5cbd038260f41dde686a93c672213881ead3ce8af3513b396',
|
'com.android.tools.lint:lint-gradle-api:27.1.3:lint-gradle-api-27.1.3.jar:e54131c287a2954e6ed78a3351e5e10e35a1da2f09ac443bf44b705c71b63a4d',
|
||||||
'com.android.tools.lint:lint:30.0.3:lint-30.0.3.jar:ee4f11001e0c7e3b776e0d67399ad354b19b0f168822ec2b7db47c0910ed227d',
|
'com.android.tools.lint:lint-gradle:27.1.3:lint-gradle-27.1.3.jar:6a79e48943649d63665db7b17dbaff7af93e94ab9b15072f1a4d90486294ee9f',
|
||||||
'com.android.tools:annotations:30.0.3:annotations-30.0.3.jar:5c1944982fda8555855c4f5422fabf0dc8e2306e1f5460e9ad82dae71316bc31',
|
'com.android.tools.lint:lint-model:27.1.3:lint-model-27.1.3.jar:acb9e792db7000e38e3c3ca21a9b14f2de6549d7a3fc92a97ffba3d06345e5bf',
|
||||||
'com.android.tools:common:30.0.3:common-30.0.3.jar:8751efaaf2c2ddd1f0a37526c794347def6a3057ca9fc510307c13a6cf0d036f',
|
'com.android.tools.lint:lint:27.1.3:lint-27.1.3.jar:5a2e69d0901a3a476a5b2d5001de755868113145f5f6aa557750cfad5389a44b',
|
||||||
'com.android.tools:dvlib:30.0.3:dvlib-30.0.3.jar:5affafcec390041e5afd64cb924153f5e474db47ee8ccc2f555b495083141233',
|
'com.android.tools:annotations:27.1.3:annotations-27.1.3.jar:904dd771883496d5dfc86619ab2555968ea4e8a29d7a5f4f7cae6fbf5429f8f5',
|
||||||
'com.android.tools:repository:30.0.3:repository-30.0.3.jar:0a40c6f16c506903ce2c609affd8228aceda73a69d93dfa42d4f02b8491449f6',
|
'com.android.tools:common:27.1.3:common-27.1.3.jar:17ab4728e3ea50f047dd5937f0faf35f2c5416962ed74891057087ddc328bf96',
|
||||||
'com.android.tools:sdk-common:30.0.3:sdk-common-30.0.3.jar:b45570a380360236ffee0f6bb593d66b673bad3834dfe0d6c9871fa7188ee0eb',
|
'com.android.tools:dvlib:27.1.3:dvlib-27.1.3.jar:cead1c0c356cbe43e6855b0330fe09ef4bec2c72e22bdb4c6e7cf7e6b1dfbc37',
|
||||||
'com.android.tools:sdklib:30.0.3:sdklib-30.0.3.jar:7088f20a414fab170a21e457825e14ebe099f753558e02c8acc12c67eb412162',
|
'com.android.tools:repository:27.1.3:repository-27.1.3.jar:99de1a178855b56b8cd91a56296f1e0a9399c445e6acc51f1d2927947cc472cb',
|
||||||
'com.android:signflinger:7.0.3:signflinger-7.0.3.jar:903a4536db3e96b4e1e1dc1e400eb0b91bf7866d9b39cd7ec94d75dde158f152',
|
'com.android.tools:sdk-common:27.1.3:sdk-common-27.1.3.jar:b591e2aa0f1be600795f5c9e2bf81cba9b052bee452fc86c3362b5dd9e427a14',
|
||||||
'com.android:zipflinger:7.0.3:zipflinger-7.0.3.jar:fd209c960a3eff7a339e6fcba07d5e9ef4604d1633c69ab2df987460d9804140',
|
'com.android.tools:sdklib:27.1.3:sdklib-27.1.3.jar:ad6c08a45fe2904d05656bdddf9f623fa5c1d16bbd7b8d6a270a0734136ae02e',
|
||||||
'com.beust:jcommander:1.78:jcommander-1.78.jar:7891debb84b5f83e9bd57593ebece3399abbe0fd938cf306b3534c57913b9615',
|
'com.android:signflinger:4.1.3:signflinger-4.1.3.jar:f3103b55ccdc8dd9ee2517eb26af93b904d41303726594372d0df59d51156e5c',
|
||||||
'com.github.javaparser:javaparser-core:3.17.0:javaparser-core-3.17.0.jar:23f5c982e1c7771423d37d52c774e8d2e80fd7ea7305ebe448797a96f67e6fca',
|
'com.android:zipflinger:4.1.3:zipflinger-4.1.3.jar:48569896c0497268308a8014c66eb0f2bace2b9e2fc9390f3012823fb86387d5',
|
||||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'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.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.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
|
||||||
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
|
'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-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-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
|
||||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
'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.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.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d',
|
||||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
'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.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: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: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:28.1-jre:guava-28.1-jre.jar:30beb8b8527bd07c6e747e77f1a92122c2f29d57ce347461a4a55eb26e382da4',
|
||||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
'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.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
'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.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.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||||
|
'com.puppycrawl.tools:checkstyle:8.27:checkstyle-8.27.jar:26c81958a112ebdfc5d7b40507bbfc8f15f606fea1e55ef5675609ddb41a6053',
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'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.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.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.istack:istack-commons-runtime:3.0.7:istack-commons-runtime-3.0.7.jar:6443e10ba2e259fb821d9b6becf10db5316285fc30c53cec9d7b19a3877e7fdf',
|
||||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.16:FastInfoset-1.2.16.jar:056f3a1e144409f21ed16afc26805f58e9a21f3fce1543c42d400719d250c511',
|
'com.sun.xml.fastinfoset:FastInfoset:1.2.15:FastInfoset-1.2.15.jar:785861db11ca1bd0d1956682b974ad73eb19cd3e01a4b3fa82d62eca97210aec',
|
||||||
'com.thoughtworks.qdox:qdox:1.12.1:qdox-1.12.1.jar:21fba22f830e9268f07cf4ab2d99e8181abbdcb0cb91ee0228eb3cb918dcdd1d',
|
'commons-beanutils:commons-beanutils:1.9.4:commons-beanutils-1.9.4.jar:7d938c81789028045c08c065e94be75fc280527620d5bd62b519d5838532368a',
|
||||||
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
|
'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-collections:commons-collections:3.2.2:commons-collections-3.2.2.jar:eeeae917917144a68a741d4c0dff66aa5c5c5fd85593ff217bced3fc8ca783b8',
|
||||||
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
||||||
'info.picocli:picocli:4.5.2:picocli-4.5.2.jar:b4395e9a67932616efd2245d984bf5fcd453c2c5049558c3ce959ac2af4d3fac',
|
'info.picocli:picocli:4.1.1:picocli-4.1.1.jar:9238b6af4ded57dcfedf6f7dbcf2bdf334dae2f118ea2a0bd6d05066e63ee6fc',
|
||||||
'it.unimi.dsi:fastutil:8.4.0:fastutil-8.4.0.jar:2ad2824a4a0a0eb836b52ee2fc84ba2134f44bce7bfa54015ae3f31c710a3071',
|
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
||||||
'jakarta.activation:jakarta.activation-api:1.2.1:jakarta.activation-api-1.2.1.jar:8b0a0f52fa8b05c5431921a063ed866efaa41dadf2e3a7ee3e1961f2b0d9645b',
|
'javax.activation:javax.activation-api:1.2.0:javax.activation-api-1.2.0.jar:43fdef0b5b6ceb31b0424b208b930c74ab58fac2ceeb7b3f6fd3aeb8b5ca4393',
|
||||||
'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.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'jline:jline:2.14.6:jline-2.14.6.jar:97d1acaac82409be42e622d7a54d3ae9d08517e8aefdea3d2ba9791150c2f02d',
|
'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06',
|
||||||
'junit:junit:4.13.1:junit-4.13.1.jar:c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122',
|
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'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.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.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
'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',
|
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||||
|
'net.sf.saxon:Saxon-HE:9.9.1-5:Saxon-HE-9.9.1-5.jar:4b39a9e30d5b634b5e91db5f0c6937f176d4517543ababc9b5e01830b8c56620',
|
||||||
|
'org.antlr:antlr4-runtime:4.7.2:antlr4-runtime-4.7.2.jar:4c518b87d4bdff8b44cd8cbc1af816e944b62a3fe5b80b781501cf1f4759bbc4',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.apache.ant:ant-antlr:1.10.9:ant-antlr-1.10.9.jar:7623dc9d0f20ea713290c6bf1a23f4c059447aef7ff9f5b2be75960f3f028d2e',
|
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
||||||
'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: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:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||||
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
|
'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:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.12:obfs4proxy-android-0.0.12.jar:84159d2a4668abc40e3fccaa1f6fa0c04892863f9eb80a866ac8928d9f9a7e89',
|
'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82',
|
||||||
'org.briarproject:tor-android:0.4.5.12-2:tor-android-0.4.5.12-2.jar:8545dbcef2bb6aa89c32bb6f8ac51f7a64bce3ae85845b3578ffdeb9b206feb9',
|
'org.briarproject:tor-android:0.3.5.15:tor-android-0.3.5.15.jar:560c5070166300b396cb2f28d82d9f639ee1fb5479096a3cef67da56d39937ad',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'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: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.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
||||||
'org.codehaus.groovy:groovy-ant:3.0.7:groovy-ant-3.0.7.jar:6ed2ba82813d128f7050c24142e87b3dc2ad8b504786280eb03e81f0cf6a5793',
|
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
|
||||||
'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.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.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
|
||||||
'org.glassfish.jaxb:txw2:2.3.2:txw2-2.3.2.jar:4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe',
|
'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f',
|
||||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'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-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
'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.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40',
|
||||||
'org.jacoco:org.jacoco.ant:0.8.3:org.jacoco.ant-0.8.3.jar:735844e1ae15f9b875b42a27ac5cb61cc26e106d9e839e5d1c6756709b424ce0',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a',
|
||||||
'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.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.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72:kotlin-stdlib-jdk8-1.3.72.jar:133da70cfc07b56094282eac5c59bccd59f167ee2ead22e5282876d8bc10bf95',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
|
'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.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
|
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
|
||||||
|
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||||
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
|
'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-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-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
|
||||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
'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.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77',
|
||||||
'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.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-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-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-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-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
|
||||||
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
||||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
'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',
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,18 @@ sourceCompatibility = 1.8
|
|||||||
targetCompatibility = 1.8
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
apply plugin: 'ru.vyarus.animalsniffer'
|
apply plugin: 'ru.vyarus.animalsniffer'
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "com.google.dagger:dagger:$dagger_version"
|
implementation "com.google.dagger:dagger:$dagger_version"
|
||||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
|
testImplementation "org.jmock:jmock-legacy:$jmock_version"
|
||||||
|
|
||||||
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package org.briarproject.bramble.api;
|
|
||||||
|
|
||||||
public interface Cancellable {
|
|
||||||
|
|
||||||
void cancel();
|
|
||||||
}
|
|
||||||
@@ -10,12 +10,4 @@ public interface FeatureFlags {
|
|||||||
boolean shouldEnableProfilePictures();
|
boolean shouldEnableProfilePictures();
|
||||||
|
|
||||||
boolean shouldEnableDisappearingMessages();
|
boolean shouldEnableDisappearingMessages();
|
||||||
|
|
||||||
boolean shouldEnableMailbox();
|
|
||||||
|
|
||||||
boolean shouldEnablePrivateGroupsInCore();
|
|
||||||
|
|
||||||
boolean shouldEnableForumsInCore();
|
|
||||||
|
|
||||||
boolean shouldEnableBlogsInCore();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
package org.briarproject.bramble.api;
|
package org.briarproject.bramble.api;
|
||||||
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public abstract class StringMap extends Hashtable<String, String> {
|
public abstract class StringMap extends Hashtable<String, String> {
|
||||||
|
|
||||||
protected StringMap(Map<String, String> m) {
|
protected StringMap(Map<String, String> m) {
|
||||||
@@ -58,31 +52,4 @@ public abstract class StringMap extends Hashtable<String, String> {
|
|||||||
public void putLong(String key, long value) {
|
public void putLong(String key, long value) {
|
||||||
put(key, String.valueOf(value));
|
put(key, String.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public int[] getIntArray(String key) {
|
|
||||||
String s = get(key);
|
|
||||||
if (s == null) return null;
|
|
||||||
// Handle empty string because "".split(",") returns {""}
|
|
||||||
if (s.length() == 0) return new int[0];
|
|
||||||
String[] intStrings = s.split(",");
|
|
||||||
int[] ints = new int[intStrings.length];
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < ints.length; i++) {
|
|
||||||
ints[i] = Integer.parseInt(intStrings[i]);
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putIntArray(String key, int[] value) {
|
|
||||||
List<String> intStrings = new ArrayList<>();
|
|
||||||
for (int integer : value) {
|
|
||||||
intStrings.add(String.valueOf(integer));
|
|
||||||
}
|
|
||||||
// Puts empty string if input array value is empty
|
|
||||||
put(key, StringUtils.join(intStrings, ","));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class UniqueId extends Bytes {
|
public abstract class UniqueId extends Bytes {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of a unique identifier in bytes.
|
* The length of a unique identifier in bytes.
|
||||||
*/
|
*/
|
||||||
public static final int LENGTH = 32;
|
public static final int LENGTH = 32;
|
||||||
|
|
||||||
public UniqueId(byte[] id) {
|
protected UniqueId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
if (id.length != LENGTH) throw new IllegalArgumentException();
|
if (id.length != LENGTH) throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package org.briarproject.bramble.api;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link Provider} that keeps a {@link WeakReference} to the last provided
|
|
||||||
* instance and provides the same instance again until the instance is garbage
|
|
||||||
* collected.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public abstract class WeakSingletonProvider<T> implements Provider<T> {
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private WeakReference<T> ref = new WeakReference<>(null);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
synchronized (lock) {
|
|
||||||
T instance = ref.get();
|
|
||||||
if (instance == null) {
|
|
||||||
instance = createInstance();
|
|
||||||
ref = new WeakReference<>(instance);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract T createInstance();
|
|
||||||
}
|
|
||||||
@@ -21,8 +21,9 @@ public interface CleanupHook {
|
|||||||
* <p>
|
* <p>
|
||||||
* The callee is not required to delete the messages, but the hook won't be
|
* The callee is not required to delete the messages, but the hook won't be
|
||||||
* called again for these messages unless another cleanup timer is set (see
|
* called again for these messages unless another cleanup timer is set (see
|
||||||
* {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)}
|
* {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId,
|
||||||
* and {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)}).
|
* long)} and {@link DatabaseComponent#startCleanupTimer(Transaction,
|
||||||
|
* MessageId)}).
|
||||||
*/
|
*/
|
||||||
void deleteMessages(Transaction txn, GroupId g,
|
void deleteMessages(Transaction txn, GroupId g,
|
||||||
Collection<MessageId> messageIds) throws DbException;
|
Collection<MessageId> messageIds) throws DbException;
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
* {@link CleanupTimerStartedEvent CleanupTimerStartedEvents} broadcast by the
|
* {@link CleanupTimerStartedEvent CleanupTimerStartedEvents} broadcast by the
|
||||||
* {@link DatabaseComponent}.
|
* {@link DatabaseComponent}.
|
||||||
* <p>
|
* <p>
|
||||||
* See {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)},
|
* See {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId,
|
||||||
* {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)},
|
* long)}, {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)},
|
||||||
* {@link DatabaseComponent#stopCleanupTimer(Transaction, MessageId)}.
|
* {@link DatabaseComponent#stopCleanupTimer(Transaction, MessageId)}.
|
||||||
*/
|
*/
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import org.briarproject.bramble.api.data.BdfList;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
@@ -20,7 +18,6 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -126,25 +123,12 @@ public interface ClientHelper {
|
|||||||
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
||||||
BdfDictionary properties) throws FormatException;
|
BdfDictionary properties) throws FormatException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse and validate the elements of a Mailbox update message.
|
|
||||||
*
|
|
||||||
* @return the parsed update message
|
|
||||||
* @throws FormatException if the message elements are invalid
|
|
||||||
*/
|
|
||||||
MailboxUpdate parseAndValidateMailboxUpdate(BdfList clientSupports,
|
|
||||||
BdfList serverSupports, BdfDictionary properties)
|
|
||||||
throws FormatException;
|
|
||||||
|
|
||||||
List<MailboxVersion> parseMailboxVersionList(BdfList bdfList)
|
|
||||||
throws FormatException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the contact ID from the group metadata of the given contact
|
* Retrieves the contact ID from the group metadata of the given contact
|
||||||
* group.
|
* group.
|
||||||
*/
|
*/
|
||||||
ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||||
throws DbException;
|
throws DbException, FormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given contact ID in the group metadata of the given contact
|
* Stores the given contact ID in the group metadata of the given contact
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ConnectionManager {
|
public interface ConnectionManager {
|
||||||
@@ -17,17 +16,6 @@ public interface ConnectionManager {
|
|||||||
*/
|
*/
|
||||||
void manageIncomingConnection(TransportId t, TransportConnectionReader r);
|
void manageIncomingConnection(TransportId t, TransportConnectionReader r);
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages an incoming connection from a contact via a mailbox.
|
|
||||||
* <p>
|
|
||||||
* This method does not mark the tag as recognised until after the data
|
|
||||||
* has been read from the {@link TransportConnectionReader}, at which
|
|
||||||
* point the {@link TagController} is called to decide whether the tag
|
|
||||||
* should be marked as recognised.
|
|
||||||
*/
|
|
||||||
void manageIncomingConnection(TransportId t, TransportConnectionReader r,
|
|
||||||
TagController c);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages an incoming connection from a contact over a duplex transport.
|
* Manages an incoming connection from a contact over a duplex transport.
|
||||||
*/
|
*/
|
||||||
@@ -46,14 +34,6 @@ public interface ConnectionManager {
|
|||||||
void manageOutgoingConnection(ContactId c, TransportId t,
|
void manageOutgoingConnection(ContactId c, TransportId t,
|
||||||
TransportConnectionWriter w);
|
TransportConnectionWriter w);
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages an outgoing connection to a contact via a mailbox. The IDs of
|
|
||||||
* any messages sent or acked are added to the given
|
|
||||||
* {@link OutgoingSessionRecord}.
|
|
||||||
*/
|
|
||||||
void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
TransportConnectionWriter w, OutgoingSessionRecord sessionRecord);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages an outgoing connection to a contact over a duplex transport.
|
* Manages an outgoing connection to a contact over a duplex transport.
|
||||||
*/
|
*/
|
||||||
@@ -66,21 +46,4 @@ public interface ConnectionManager {
|
|||||||
*/
|
*/
|
||||||
void manageOutgoingConnection(PendingContactId p, TransportId t,
|
void manageOutgoingConnection(PendingContactId p, TransportId t,
|
||||||
DuplexTransportConnection d);
|
DuplexTransportConnection d);
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for controlling whether a tag should be marked as
|
|
||||||
* recognised.
|
|
||||||
*/
|
|
||||||
interface TagController {
|
|
||||||
/**
|
|
||||||
* This method is only called if a tag was read from the corresponding
|
|
||||||
* {@link TransportConnectionReader} and recognised.
|
|
||||||
*
|
|
||||||
* @param exception True if an exception was thrown while reading from
|
|
||||||
* the {@link TransportConnectionReader}, after successfully reading
|
|
||||||
* and recognising the tag.
|
|
||||||
* @return True if the tag should be marked as recognised.
|
|
||||||
*/
|
|
||||||
boolean shouldMarkTagAsRecognised(boolean exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,32 +107,6 @@ public interface ContactManager {
|
|||||||
*/
|
*/
|
||||||
String getHandshakeLink() throws DbException;
|
String getHandshakeLink() throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the handshake link that needs to be sent to a contact we want
|
|
||||||
* to add.
|
|
||||||
*/
|
|
||||||
String getHandshakeLink(Transaction txn) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link PendingContact} from the given handshake link and
|
|
||||||
* alias, adds it to the database and returns it.
|
|
||||||
*
|
|
||||||
* @param link The handshake link received from the pending contact
|
|
||||||
* @param alias The alias the user has given this pending contact
|
|
||||||
* @throws UnsupportedVersionException If the link uses a format version
|
|
||||||
* that is not supported
|
|
||||||
* @throws FormatException If the link is invalid
|
|
||||||
* @throws GeneralSecurityException If the pending contact's handshake
|
|
||||||
* public key is invalid
|
|
||||||
* @throws ContactExistsException If a contact with the same handshake
|
|
||||||
* public key already exists
|
|
||||||
* @throws PendingContactExistsException If a pending contact with the same
|
|
||||||
* handshake public key already exists
|
|
||||||
*/
|
|
||||||
PendingContact addPendingContact(Transaction txn, String link, String alias)
|
|
||||||
throws DbException, FormatException, GeneralSecurityException,
|
|
||||||
ContactExistsException, PendingContactExistsException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link PendingContact} from the given handshake link and
|
* Creates a {@link PendingContact} from the given handshake link and
|
||||||
* alias, adds it to the database and returns it.
|
* alias, adds it to the database and returns it.
|
||||||
@@ -166,24 +140,11 @@ public interface ContactManager {
|
|||||||
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
|
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of {@link PendingContact PendingContacts} and their
|
|
||||||
* {@link PendingContactState states}.
|
|
||||||
*/
|
|
||||||
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts(Transaction txn)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@link PendingContact}.
|
* Removes a {@link PendingContact}.
|
||||||
*/
|
*/
|
||||||
void removePendingContact(PendingContactId p) throws DbException;
|
void removePendingContact(PendingContactId p) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a {@link PendingContact}.
|
|
||||||
*/
|
|
||||||
void removePendingContact(Transaction txn, PendingContactId p)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.api.contact;
|
|||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,4 +17,9 @@ public class PendingContactId extends UniqueId {
|
|||||||
public PendingContactId(byte[] id) {
|
public PendingContactId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
return o instanceof PendingContactId && super.equals(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -11,8 +10,6 @@ import javax.annotation.Nullable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface CryptoComponent {
|
public interface CryptoComponent {
|
||||||
|
|
||||||
UniqueId generateUniqueId();
|
|
||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
|
|
||||||
SecureRandom getSecureRandom();
|
SecureRandom getSecureRandom();
|
||||||
@@ -173,13 +170,4 @@ public interface CryptoComponent {
|
|||||||
* length. The line terminator is CRLF.
|
* length. The line terminator is CRLF.
|
||||||
*/
|
*/
|
||||||
String asciiArmour(byte[] b, int lineLength);
|
String asciiArmour(byte[] b, int lineLength);
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode the Onion given its public key. Specified here:
|
|
||||||
* https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135
|
|
||||||
*
|
|
||||||
* @return the encoded onion, base32 chars
|
|
||||||
*/
|
|
||||||
String encodeOnion(byte[] publicKey);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,18 +33,11 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the database implementation and exposes high-level operations
|
* Encapsulates the database implementation and exposes high-level operations
|
||||||
* to other components.
|
* to other components.
|
||||||
* <p>
|
|
||||||
* With the exception of the {@link #open(SecretKey, MigrationListener)} and
|
|
||||||
* {@link #close()} methods, which must not be called concurrently, the
|
|
||||||
* database can be accessed from any thread. See {@link TransactionManager}
|
|
||||||
* for locking behaviour.
|
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface DatabaseComponent extends TransactionManager {
|
public interface DatabaseComponent extends TransactionManager {
|
||||||
|
|
||||||
@@ -126,11 +119,16 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
TransportKeys k) throws DbException;
|
TransportKeys k) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there are any acks to send to the given contact.
|
* Returns true if there are any acks or messages to send to the given
|
||||||
|
* contact over a transport with the given maximum latency.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
|
*
|
||||||
|
* @param eager True if messages that are not yet due for retransmission
|
||||||
|
* should be included
|
||||||
*/
|
*/
|
||||||
boolean containsAcksToSend(Transaction txn, ContactId c) throws DbException;
|
boolean containsAnythingToSend(Transaction txn, ContactId c,
|
||||||
|
long maxLatency, boolean eager) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact for the given
|
||||||
@@ -156,18 +154,6 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
*/
|
*/
|
||||||
boolean containsIdentity(Transaction txn, AuthorId a) throws DbException;
|
boolean containsIdentity(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if there are any messages to send to the given contact
|
|
||||||
* over a transport with the given maximum latency.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*
|
|
||||||
* @param eager True if messages that are not yet due for retransmission
|
|
||||||
* should be included
|
|
||||||
*/
|
|
||||||
boolean containsMessagesToSend(Transaction txn, ContactId c,
|
|
||||||
long maxLatency, boolean eager) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given pending contact.
|
* Returns true if the database contains the given pending contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -207,15 +193,26 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a batch of messages for the given contact, for transmission over
|
* Returns a batch of messages for the given contact, with a total length
|
||||||
* a transport with the given maximum latency. The total length of the
|
* less than or equal to the given length, for transmission over a
|
||||||
* messages, including record headers, will be no more than the given
|
* transport with the given maximum latency. Returns null if there are no
|
||||||
* capacity. Returns null if there are no sendable messages that would fit
|
* sendable messages that fit in the given length.
|
||||||
* in the given capacity.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Collection<Message> generateBatch(Transaction txn, ContactId c,
|
Collection<Message> generateBatch(Transaction txn, ContactId c,
|
||||||
long capacity, long maxLatency) throws DbException;
|
int maxLength, long maxLatency) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a batch of messages for the given contact containing the
|
||||||
|
* messages with the given IDs, for transmission over a transport with
|
||||||
|
* the given maximum latency.
|
||||||
|
* <p/>
|
||||||
|
* If any of the given messages are not in the database or are not visible
|
||||||
|
* to the contact, they are omitted from the batch without throwing an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
Collection<Message> generateBatch(Transaction txn, ContactId c,
|
||||||
|
Collection<MessageId> ids, long maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an offer for the given contact for transmission over a
|
* Returns an offer for the given contact for transmission over a
|
||||||
@@ -235,16 +232,15 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a batch of messages for the given contact, for transmission over
|
* Returns a batch of messages for the given contact, with a total length
|
||||||
* a transport with the given maximum latency. Only messages that have been
|
* less than or equal to the given length, for transmission over a
|
||||||
* requested by the contact are returned. The total length of the messages,
|
* transport with the given maximum latency. Only messages that have been
|
||||||
* including record headers, will be no more than the given capacity.
|
* requested by the contact are returned. Returns null if there are no
|
||||||
* Returns null if there are no sendable messages that have been requested
|
* sendable messages that fit in the given length.
|
||||||
* by the contact and would fit in the given capacity.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
||||||
long capacity, long maxLatency) throws DbException;
|
int maxLength, long maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
@@ -348,30 +344,6 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||||
Metadata query) throws DbException;
|
Metadata query) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the IDs of some messages received from the given contact that
|
|
||||||
* need to be acknowledged, up to the given number of messages.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
Collection<MessageId> getMessagesToAck(Transaction txn, ContactId c,
|
|
||||||
int maxMessages) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the IDs of some messages that are eligible to be sent to the
|
|
||||||
* given contact over a transport with the given maximum latency. The total
|
|
||||||
* length of the messages including record headers will be no more than the
|
|
||||||
* given capacity.
|
|
||||||
* <p/>
|
|
||||||
* Unlike {@link #getUnackedMessagesToSend(Transaction, ContactId)} this
|
|
||||||
* method does not return messages that have already been sent unless they
|
|
||||||
* are due for retransmission.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
Collection<MessageId> getMessagesToSend(Transaction txn, ContactId c,
|
|
||||||
long capacity, long maxLatency) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that need to be validated.
|
* Returns the IDs of any messages that need to be validated.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -488,40 +460,17 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the message with the given ID for transmission to the given
|
|
||||||
* contact over a transport with the given maximum latency. Returns null
|
|
||||||
* if the message is no longer visible to the contact.
|
|
||||||
*
|
|
||||||
* @param markAsSent True if the message should be marked as sent.
|
|
||||||
* If false it can be marked as sent by calling
|
|
||||||
* {@link #setMessagesSent(Transaction, ContactId, Collection, long)}.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Message getMessageToSend(Transaction txn, ContactId c, MessageId m,
|
|
||||||
long maxLatency, boolean markAsSent) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of all messages that are eligible to be sent to the
|
* Returns the IDs of all messages that are eligible to be sent to the
|
||||||
* given contact.
|
* given contact, together with their raw lengths. This may include
|
||||||
* <p>
|
* messages that have already been sent and are not yet due for
|
||||||
* Unlike {@link #getMessagesToSend(Transaction, ContactId, long, long)}
|
* retransmission.
|
||||||
* this method may return messages that have already been sent and are
|
|
||||||
* not yet due for retransmission.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getUnackedMessagesToSend(Transaction txn,
|
Map<MessageId, Integer> getUnackedMessagesToSend(Transaction txn,
|
||||||
ContactId c) throws DbException;
|
ContactId c) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the transmission count, expiry time and max latency of all messages
|
|
||||||
* that are eligible to be sent to the given contact. This includes messages
|
|
||||||
* that have already been sent and are not yet due for retransmission.
|
|
||||||
*/
|
|
||||||
void resetUnackedMessagesToSend(Transaction txn, ContactId c)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total length, including headers, of all messages that are
|
* Returns the total length, including headers, of all messages that are
|
||||||
* eligible to be sent to the given contact. This may include messages
|
* eligible to be sent to the given contact. This may include messages
|
||||||
@@ -541,18 +490,15 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
*/
|
*/
|
||||||
long getNextCleanupDeadline(Transaction txn) throws DbException;
|
long getNextCleanupDeadline(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||||
* message is due to be sent to the given contact over a transport with
|
* message is due to be sent to the given contact. The returned value may
|
||||||
* the given latency.
|
* be zero if a message is due to be sent immediately, or Long.MAX_VALUE if
|
||||||
* <p>
|
* no messages are scheduled to be sent.
|
||||||
* The returned value may be zero if a message is due to be sent
|
|
||||||
* immediately, or Long.MAX_VALUE if no messages are scheduled to be sent.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
long getNextSendTime(Transaction txn, ContactId c, long maxLatency)
|
long getNextSendTime(Transaction txn, ContactId c) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pending contact with the given ID.
|
* Returns the pending contact with the given ID.
|
||||||
@@ -694,13 +640,6 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Records an ack for the given messages as having been sent to the given
|
|
||||||
* contact.
|
|
||||||
*/
|
|
||||||
void setAckSent(Transaction txn, ContactId c, Collection<MessageId> acked)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cleanup timer duration for the given message. This does not
|
* Sets the cleanup timer duration for the given message. This does not
|
||||||
* start the message's cleanup timer.
|
* start the message's cleanup timer.
|
||||||
@@ -747,13 +686,6 @@ public interface DatabaseComponent extends TransactionManager {
|
|||||||
void setMessageState(Transaction txn, MessageId m, MessageState state)
|
void setMessageState(Transaction txn, MessageId m, MessageState state)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Records the given messages as having been sent to the given contact
|
|
||||||
* over a transport with the given maximum latency.
|
|
||||||
*/
|
|
||||||
void setMessagesSent(Transaction txn, ContactId c,
|
|
||||||
Collection<MessageId> sent, long maxLatency) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds dependencies for a message
|
* Adds dependencies for a message
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
* submitted, tasks are not run concurrently, and submitting a task will never
|
* submitted, tasks are not run concurrently, and submitting a task will never
|
||||||
* block. Tasks must not run indefinitely. Tasks submitted during shutdown are
|
* block. Tasks must not run indefinitely. Tasks submitted during shutdown are
|
||||||
* discarded.
|
* discarded.
|
||||||
* <p>
|
|
||||||
* It is not mandatory to use this executor for database tasks. The database
|
|
||||||
* can be accessed from any thread, but this executor's guarantee that tasks
|
|
||||||
* are run in the order they're submitted may be useful in some cases.
|
|
||||||
*/
|
*/
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Target({FIELD, METHOD, PARAMETER})
|
@Target({FIELD, METHOD, PARAMETER})
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ public class Transaction {
|
|||||||
/**
|
/**
|
||||||
* Attaches an event to be broadcast when the transaction has been
|
* Attaches an event to be broadcast when the transaction has been
|
||||||
* committed. The event will be broadcast on the {@link EventExecutor}.
|
* committed. The event will be broadcast on the {@link EventExecutor}.
|
||||||
* Events and {@link #attach(Runnable) tasks} are submitted to the
|
|
||||||
* {@link EventExecutor} in the order they were attached to the
|
|
||||||
* transaction.
|
|
||||||
*/
|
*/
|
||||||
public void attach(Event e) {
|
public void attach(Event e) {
|
||||||
if (actions == null) actions = new ArrayList<>();
|
if (actions == null) actions = new ArrayList<>();
|
||||||
@@ -57,9 +54,6 @@ public class Transaction {
|
|||||||
/**
|
/**
|
||||||
* Attaches a task to be executed when the transaction has been
|
* Attaches a task to be executed when the transaction has been
|
||||||
* committed. The task will be run on the {@link EventExecutor}.
|
* committed. The task will be run on the {@link EventExecutor}.
|
||||||
* {@link #attach(Event) Events} and tasks are submitted to the
|
|
||||||
* {@link EventExecutor} in the order they were attached to the
|
|
||||||
* transaction.
|
|
||||||
*/
|
*/
|
||||||
public void attach(Runnable r) {
|
public void attach(Runnable r) {
|
||||||
if (actions == null) actions = new ArrayList<>();
|
if (actions == null) actions = new ArrayList<>();
|
||||||
|
|||||||
@@ -1,95 +1,51 @@
|
|||||||
package org.briarproject.bramble.api.db;
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for managing database transactions.
|
|
||||||
* <p>
|
|
||||||
* Read-only transactions may access the database concurrently. Read-write
|
|
||||||
* transactions access the database exclusively, so starting a read-only or
|
|
||||||
* read-write transaction will block until there are no read-write
|
|
||||||
* transactions in progress.
|
|
||||||
* <p>
|
|
||||||
* Failing to {@link #endTransaction(Transaction) end} a transaction will
|
|
||||||
* prevent other callers from accessing the database, so it is recommended to
|
|
||||||
* use the {@link #transaction(boolean, DbRunnable)},
|
|
||||||
* {@link #transactionWithResult(boolean, DbCallable)} and
|
|
||||||
* {@link #transactionWithNullableResult(boolean, NullableDbCallable)} methods
|
|
||||||
* where possible, which handle committing or aborting the transaction on the
|
|
||||||
* caller's behalf.
|
|
||||||
* <p>
|
|
||||||
* Transactions are not reentrant, i.e. it is not permitted to start a
|
|
||||||
* transaction on a thread that already has a transaction in progress.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface TransactionManager {
|
public interface TransactionManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a new transaction and returns an object representing it. This
|
* Starts a new transaction and returns an object representing it.
|
||||||
* method acquires the database lock, which is held until
|
* <p/>
|
||||||
* {@link #endTransaction(Transaction)} is called.
|
* This method acquires locks, so it must not be called while holding a
|
||||||
|
* lock.
|
||||||
*
|
*
|
||||||
* @param readOnly True if the transaction will only be used for reading,
|
* @param readOnly true if the transaction will only be used for reading.
|
||||||
* in which case the database lock can be shared with other read-only
|
|
||||||
* transactions.
|
|
||||||
*/
|
*/
|
||||||
Transaction startTransaction(boolean readOnly) throws DbException;
|
Transaction startTransaction(boolean readOnly) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits a transaction to the database.
|
* Commits a transaction to the database.
|
||||||
* {@link #endTransaction(Transaction)} must be called to release the
|
|
||||||
* database lock.
|
|
||||||
*/
|
*/
|
||||||
void commitTransaction(Transaction txn) throws DbException;
|
void commitTransaction(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends a transaction. If the transaction has not been committed by
|
* Ends a transaction. If the transaction has not been committed,
|
||||||
* calling {@link #commitTransaction(Transaction)}, it is aborted and the
|
* it will be aborted. If the transaction has been committed,
|
||||||
* database lock is released.
|
* any events attached to the transaction are broadcast.
|
||||||
* <p>
|
* The database lock will be released in either case.
|
||||||
* If the transaction has been committed, any
|
|
||||||
* {@link Transaction#attach events} attached to the transaction are
|
|
||||||
* broadcast and any {@link Transaction#attach(Runnable) tasks} attached
|
|
||||||
* to the transaction are submitted to the {@link EventExecutor}. The
|
|
||||||
* database lock is then released.
|
|
||||||
*/
|
*/
|
||||||
void endTransaction(Transaction txn);
|
void endTransaction(Transaction txn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given task within a transaction. The database lock is held
|
* Runs the given task within a transaction.
|
||||||
* while running the task.
|
|
||||||
*
|
|
||||||
* @param readOnly True if the transaction will only be used for reading,
|
|
||||||
* in which case the database lock can be shared with other read-only
|
|
||||||
* transactions.
|
|
||||||
*/
|
*/
|
||||||
<E extends Exception> void transaction(boolean readOnly,
|
<E extends Exception> void transaction(boolean readOnly,
|
||||||
DbRunnable<E> task) throws DbException, E;
|
DbRunnable<E> task) throws DbException, E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given task within a transaction and returns the result of the
|
* Runs the given task within a transaction and returns the result of the
|
||||||
* task. The database lock is held while running the task.
|
* task.
|
||||||
*
|
|
||||||
* @param readOnly True if the transaction will only be used for reading,
|
|
||||||
* in which case the database lock can be shared with other read-only
|
|
||||||
* transactions.
|
|
||||||
*/
|
*/
|
||||||
<R, E extends Exception> R transactionWithResult(boolean readOnly,
|
<R, E extends Exception> R transactionWithResult(boolean readOnly,
|
||||||
DbCallable<R, E> task) throws DbException, E;
|
DbCallable<R, E> task) throws DbException, E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given task within a transaction and returns the result of the
|
* Runs the given task within a transaction and returns the result of the
|
||||||
* task, which may be null. The database lock is held while running the
|
* task, which may be null.
|
||||||
* task.
|
|
||||||
*
|
|
||||||
* @param readOnly True if the transaction will only be used for reading,
|
|
||||||
* in which case the database lock can be shared with other read-only
|
|
||||||
* transactions.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
<R, E extends Exception> R transactionWithNullableResult(boolean readOnly,
|
<R, E extends Exception> R transactionWithNullableResult(boolean readOnly,
|
||||||
|
|||||||
@@ -21,4 +21,9 @@ public class AuthorId extends UniqueId {
|
|||||||
public AuthorId(byte[] id) {
|
public AuthorId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof AuthorId && super.equals(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class InvalidMailboxIdException extends Exception {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxAuthToken extends MailboxId {
|
|
||||||
public MailboxAuthToken(byte[] id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link MailboxAuthToken} from the given string.
|
|
||||||
*
|
|
||||||
* @throws InvalidMailboxIdException if token is not valid.
|
|
||||||
*/
|
|
||||||
public static MailboxAuthToken fromString(@Nullable String token)
|
|
||||||
throws InvalidMailboxIdException {
|
|
||||||
return new MailboxAuthToken(bytesFromString(token));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.concurrent.TimeUnit.DAYS;
|
|
||||||
import static java.util.concurrent.TimeUnit.HOURS;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
|
|
||||||
public interface MailboxConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The transport ID of the mailbox plugin.
|
|
||||||
*/
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mailbox API versions that we support as a client. This is reported to our
|
|
||||||
* contacts by {@link MailboxUpdateManager}.
|
|
||||||
*/
|
|
||||||
List<MailboxVersion> CLIENT_SUPPORTS = singletonList(
|
|
||||||
new MailboxVersion(1, 0));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The constant returned by
|
|
||||||
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}
|
|
||||||
* when the server is too old to support our major version.
|
|
||||||
*/
|
|
||||||
int API_SERVER_TOO_OLD = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The constant returned by
|
|
||||||
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}
|
|
||||||
* when we as a client are too old to support the server's major version.
|
|
||||||
*/
|
|
||||||
int API_CLIENT_TOO_OLD = -2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum length of a file that can be uploaded to or downloaded from
|
|
||||||
* a mailbox.
|
|
||||||
*/
|
|
||||||
int MAX_FILE_BYTES = 1024 * 1024;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum length of the plaintext payload of a file, such that the
|
|
||||||
* ciphertext is no more than {@link #MAX_FILE_BYTES}.
|
|
||||||
*/
|
|
||||||
int MAX_FILE_PAYLOAD_BYTES =
|
|
||||||
(MAX_FILE_BYTES - TAG_LENGTH - STREAM_HEADER_LENGTH)
|
|
||||||
/ MAX_FRAME_LENGTH * MAX_PAYLOAD_LENGTH;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of connection failures
|
|
||||||
* that indicate a problem with the mailbox.
|
|
||||||
*/
|
|
||||||
int PROBLEM_NUM_CONNECTION_FAILURES = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time in milliseconds since the last connection success
|
|
||||||
* that need to pass to indicates a problem with the mailbox.
|
|
||||||
*/
|
|
||||||
long PROBLEM_MS_SINCE_LAST_SUCCESS = HOURS.toMillis(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum latency of the mailbox transport in milliseconds.
|
|
||||||
*/
|
|
||||||
long MAX_LATENCY = DAYS.toMillis(14);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
import javax.inject.Qualifier;
|
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
|
||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation for injecting the {@link File directory} where the Mailbox plugin
|
|
||||||
* should store its state.
|
|
||||||
*/
|
|
||||||
@Qualifier
|
|
||||||
@Target({FIELD, METHOD, PARAMETER})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface MailboxDirectory {
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxFileId extends MailboxId {
|
|
||||||
public MailboxFileId(byte[] id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link MailboxFileId} from the given string.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if token is not valid.
|
|
||||||
*/
|
|
||||||
public static MailboxFileId fromString(@Nullable String token)
|
|
||||||
throws InvalidMailboxIdException {
|
|
||||||
return new MailboxFileId(bytesFromString(token));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxFolderId extends MailboxId {
|
|
||||||
public MailboxFolderId(byte[] id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link MailboxFolderId} from the given string.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if token is not valid.
|
|
||||||
*/
|
|
||||||
public static MailboxFolderId fromString(@Nullable String token)
|
|
||||||
throws InvalidMailboxIdException {
|
|
||||||
return new MailboxFolderId(bytesFromString(token));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_SERVER_TOO_OLD;
|
|
||||||
|
|
||||||
class MailboxHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the highest major version that both client and server support
|
|
||||||
* or {@link MailboxConstants#API_SERVER_TOO_OLD} if the server is too old
|
|
||||||
* or {@link MailboxConstants#API_CLIENT_TOO_OLD} if the client is too old.
|
|
||||||
*/
|
|
||||||
static int getHighestCommonMajorVersion(
|
|
||||||
List<MailboxVersion> client, List<MailboxVersion> server) {
|
|
||||||
TreeSet<Integer> clientVersions = new TreeSet<>();
|
|
||||||
for (MailboxVersion version : client) {
|
|
||||||
clientVersions.add(version.getMajor());
|
|
||||||
}
|
|
||||||
TreeSet<Integer> serverVersions = new TreeSet<>();
|
|
||||||
for (MailboxVersion version : server) {
|
|
||||||
serverVersions.add(version.getMajor());
|
|
||||||
}
|
|
||||||
for (int clientVersion : clientVersions.descendingSet()) {
|
|
||||||
if (serverVersions.contains(clientVersion)) return clientVersion;
|
|
||||||
}
|
|
||||||
if (clientVersions.last() < serverVersions.last()) {
|
|
||||||
return API_CLIENT_TOO_OLD;
|
|
||||||
}
|
|
||||||
return API_SERVER_TOO_OLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonValue;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
public abstract class MailboxId extends UniqueId {
|
|
||||||
MailboxId(byte[] id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns valid {@link MailboxId} bytes from the given string.
|
|
||||||
*
|
|
||||||
* @throws InvalidMailboxIdException if token is not valid.
|
|
||||||
*/
|
|
||||||
static byte[] bytesFromString(@Nullable String token)
|
|
||||||
throws InvalidMailboxIdException {
|
|
||||||
if (token == null || token.length() != 64) {
|
|
||||||
throw new InvalidMailboxIdException();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return fromHexString(token);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new InvalidMailboxIdException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the string representation expected by the mailbox API.
|
|
||||||
* Also used for serialization.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@JsonValue
|
|
||||||
public String toString() {
|
|
||||||
return toHexString(getBytes()).toLowerCase(Locale.US);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public interface MailboxManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if a mailbox is already paired.
|
|
||||||
*/
|
|
||||||
boolean isPaired(Transaction txn) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the current status of the mailbox.
|
|
||||||
*/
|
|
||||||
MailboxStatus getMailboxStatus(Transaction txn) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently running pairing task,
|
|
||||||
* or null if no pairing task is running.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
MailboxPairingTask getCurrentPairingTask();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts and returns a pairing task. If a pairing task is already running,
|
|
||||||
* it will be returned and the argument will be ignored.
|
|
||||||
*
|
|
||||||
* @param qrCodePayload The ISO-8859-1 encoded bytes of the mailbox QR code.
|
|
||||||
*/
|
|
||||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be used by the UI to test the mailbox connection.
|
|
||||||
*
|
|
||||||
* @return true (success) or false (error).
|
|
||||||
* A {@link OwnMailboxConnectionStatusEvent} might be broadcast with a new
|
|
||||||
* {@link MailboxStatus}.
|
|
||||||
*/
|
|
||||||
boolean checkConnection();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpairs the owner's mailbox and tries to wipe it.
|
|
||||||
* As this makes a network call, it should be run on the {@link IoExecutor}.
|
|
||||||
*
|
|
||||||
* @return true if we could wipe the mailbox, false if we couldn't.
|
|
||||||
* It is advised to inform the user to wipe the mailbox themselves,
|
|
||||||
* if we failed to wipe it.
|
|
||||||
*/
|
|
||||||
@IoExecutor
|
|
||||||
boolean unPair() throws DbException;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
public abstract class MailboxPairingState {
|
|
||||||
|
|
||||||
public static class QrCodeReceived extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Pairing extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Paired extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InvalidQrCode extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConnectionError extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UnexpectedError extends MailboxPairingState {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Consumer;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface MailboxPairingTask extends Runnable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an observer to the task. The observer will be notified on the
|
|
||||||
* event thread of the current state of the task and any subsequent state
|
|
||||||
* changes.
|
|
||||||
*/
|
|
||||||
void addObserver(Consumer<MailboxPairingState> observer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an observer from the task.
|
|
||||||
*/
|
|
||||||
void removeObserver(Consumer<MailboxPairingState> observer);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxProperties {
|
|
||||||
|
|
||||||
private final String baseUrl;
|
|
||||||
private final MailboxAuthToken authToken;
|
|
||||||
private final boolean owner;
|
|
||||||
private final List<MailboxVersion> serverSupports;
|
|
||||||
@Nullable
|
|
||||||
private final MailboxFolderId inboxId; // Null for own mailbox
|
|
||||||
@Nullable
|
|
||||||
private final MailboxFolderId outboxId; // Null for own mailbox
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for properties used by the mailbox's owner.
|
|
||||||
*/
|
|
||||||
public MailboxProperties(String baseUrl, MailboxAuthToken authToken,
|
|
||||||
List<MailboxVersion> serverSupports) {
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
this.authToken = authToken;
|
|
||||||
this.owner = true;
|
|
||||||
this.serverSupports = serverSupports;
|
|
||||||
this.inboxId = null;
|
|
||||||
this.outboxId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for properties used by a contact of the mailbox's owner.
|
|
||||||
*/
|
|
||||||
public MailboxProperties(String baseUrl, MailboxAuthToken authToken,
|
|
||||||
List<MailboxVersion> serverSupports, MailboxFolderId inboxId,
|
|
||||||
MailboxFolderId outboxId) {
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
this.authToken = authToken;
|
|
||||||
this.owner = false;
|
|
||||||
this.serverSupports = serverSupports;
|
|
||||||
this.inboxId = inboxId;
|
|
||||||
this.outboxId = outboxId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBaseUrl() {
|
|
||||||
return baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOnion() {
|
|
||||||
return baseUrl.replaceFirst("^http://", "")
|
|
||||||
.replaceFirst("\\.onion$", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MailboxAuthToken getAuthToken() {
|
|
||||||
return authToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOwner() {
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MailboxVersion> getServerSupports() {
|
|
||||||
return serverSupports;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MailboxFolderId getInboxId() {
|
|
||||||
return inboxId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MailboxFolderId getOutboxId() {
|
|
||||||
return outboxId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface MailboxSettingsManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a hook to be called when a mailbox has been paired or unpaired.
|
|
||||||
* This method should be called before
|
|
||||||
* {@link LifecycleManager#startServices(SecretKey)}.
|
|
||||||
*/
|
|
||||||
void registerMailboxHook(MailboxHook hook);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
MailboxProperties getOwnMailboxProperties(Transaction txn)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
void setOwnMailboxProperties(Transaction txn, MailboxProperties p)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
void removeOwnMailboxProperties(Transaction txn) throws DbException;
|
|
||||||
|
|
||||||
MailboxStatus getOwnMailboxStatus(Transaction txn) throws DbException;
|
|
||||||
|
|
||||||
void recordSuccessfulConnection(Transaction txn, long now)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
void recordSuccessfulConnection(Transaction txn, long now,
|
|
||||||
List<MailboxVersion> versions) throws DbException;
|
|
||||||
|
|
||||||
void recordFailedConnectionAttempt(Transaction txn, long now)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
void setPendingUpload(Transaction txn, ContactId id,
|
|
||||||
@Nullable String filename) throws DbException;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getPendingUpload(Transaction txn, ContactId id) throws DbException;
|
|
||||||
|
|
||||||
interface MailboxHook {
|
|
||||||
/**
|
|
||||||
* Called when Briar is paired with a mailbox
|
|
||||||
*
|
|
||||||
* @param txn A read-write transaction
|
|
||||||
* @param ownOnion Our new mailbox's onion (56 base32 chars)
|
|
||||||
*/
|
|
||||||
void mailboxPaired(Transaction txn, String ownOnion,
|
|
||||||
List<MailboxVersion> serverSupports)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the mailbox is unpaired
|
|
||||||
*
|
|
||||||
* @param txn A read-write transaction
|
|
||||||
*/
|
|
||||||
void mailboxUnpaired(Transaction txn) throws DbException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_MS_SINCE_LAST_SUCCESS;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_NUM_CONNECTION_FAILURES;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxHelper.getHighestCommonMajorVersion;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxStatus {
|
|
||||||
|
|
||||||
private final long lastAttempt, lastSuccess;
|
|
||||||
private final int attemptsSinceSuccess;
|
|
||||||
private final List<MailboxVersion> serverSupports;
|
|
||||||
|
|
||||||
public MailboxStatus(long lastAttempt, long lastSuccess,
|
|
||||||
int attemptsSinceSuccess,
|
|
||||||
List<MailboxVersion> serverSupports) {
|
|
||||||
this.lastAttempt = lastAttempt;
|
|
||||||
this.lastSuccess = lastSuccess;
|
|
||||||
this.attemptsSinceSuccess = attemptsSinceSuccess;
|
|
||||||
this.serverSupports = serverSupports;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the time of the last attempt to connect to the mailbox, in
|
|
||||||
* milliseconds since the Unix epoch, or -1 if no attempt has been made.
|
|
||||||
* <p>
|
|
||||||
* If an attempt is in progress and has not yet succeeded or failed then
|
|
||||||
* this method returns the time of the previous attempt, or -1 if the
|
|
||||||
* current attempt is the first.
|
|
||||||
*/
|
|
||||||
public long getTimeOfLastAttempt() {
|
|
||||||
return lastAttempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the time of the last successful attempt to connect to the
|
|
||||||
* mailbox, in milliseconds since the Unix epoch, or -1 if no attempt has
|
|
||||||
* succeeded.
|
|
||||||
* <p>
|
|
||||||
* If the last attempt was successful then this method returns the same
|
|
||||||
* value as {@link #getTimeOfLastAttempt()}. If an attempt is in progress
|
|
||||||
* and has not yet succeeded or failed then this method returns the time
|
|
||||||
* of the previous successful connection, or -1 if no attempt has
|
|
||||||
* succeeded.
|
|
||||||
*/
|
|
||||||
public long getTimeOfLastSuccess() {
|
|
||||||
return lastSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of attempts to connect to the mailbox that have
|
|
||||||
* failed since the last attempt succeeded, or the number of attempts that
|
|
||||||
* have been made, if no attempt has ever succeeded.
|
|
||||||
* <p>
|
|
||||||
* If an attempt is in progress and has not yet succeeded or failed then
|
|
||||||
* it is not included in this count.
|
|
||||||
*/
|
|
||||||
public int getAttemptsSinceSuccess() {
|
|
||||||
return attemptsSinceSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if this status indicates a problem with the mailbox.
|
|
||||||
*/
|
|
||||||
public boolean hasProblem(long now) {
|
|
||||||
return attemptsSinceSuccess >= PROBLEM_NUM_CONNECTION_FAILURES &&
|
|
||||||
(now - lastSuccess) >= PROBLEM_MS_SINCE_LAST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a positive integer if the mailbox is compatible. Same result as
|
|
||||||
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}.
|
|
||||||
*/
|
|
||||||
public int getMailboxCompatibility() {
|
|
||||||
return getHighestCommonMajorVersion(CLIENT_SUPPORTS, serverSupports);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxUpdate {
|
|
||||||
private final boolean hasMailbox;
|
|
||||||
private final List<MailboxVersion> clientSupports;
|
|
||||||
|
|
||||||
public MailboxUpdate(List<MailboxVersion> clientSupports) {
|
|
||||||
this(clientSupports, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
MailboxUpdate(List<MailboxVersion> clientSupports, boolean hasMailbox) {
|
|
||||||
this.clientSupports = clientSupports;
|
|
||||||
this.hasMailbox = hasMailbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MailboxVersion> getClientSupports() {
|
|
||||||
return clientSupports;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasMailbox() {
|
|
||||||
return hasMailbox;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface MailboxUpdateManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unique ID of the mailbox update (properties) client.
|
|
||||||
*/
|
|
||||||
ClientId CLIENT_ID =
|
|
||||||
new ClientId("org.briarproject.bramble.mailbox.properties");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current major version of the mailbox update (properties) client.
|
|
||||||
*/
|
|
||||||
int MAJOR_VERSION = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current minor version of the mailbox update (properties) client.
|
|
||||||
*/
|
|
||||||
int MINOR_VERSION = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of properties required for an update message with a mailbox.
|
|
||||||
*/
|
|
||||||
int PROP_COUNT = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The required properties of an update message with a mailbox.
|
|
||||||
*/
|
|
||||||
String PROP_KEY_ONION = "onion";
|
|
||||||
String PROP_KEY_AUTHTOKEN = "authToken";
|
|
||||||
String PROP_KEY_INBOXID = "inboxId";
|
|
||||||
String PROP_KEY_OUTBOXID = "outboxId";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the Onion property.
|
|
||||||
*/
|
|
||||||
int PROP_ONION_LENGTH = 56;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message metadata key for the version number of a local or remote update,
|
|
||||||
* as a BDF long.
|
|
||||||
*/
|
|
||||||
String MSG_KEY_VERSION = "version";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message metadata key for whether an update is local or remote, as a BDF
|
|
||||||
* boolean.
|
|
||||||
*/
|
|
||||||
String MSG_KEY_LOCAL = "local";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Key in the client's local group for storing the clientSupports list that
|
|
||||||
* was last sent out.
|
|
||||||
*/
|
|
||||||
String GROUP_KEY_SENT_CLIENT_SUPPORTS = "sentClientSupports";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the latest {@link MailboxUpdate} sent to the given contact.
|
|
||||||
* <p>
|
|
||||||
* If we have our own mailbox then the update will be a
|
|
||||||
* {@link MailboxUpdateWithMailbox} containing the
|
|
||||||
* {@link MailboxProperties} the contact should use for communicating with
|
|
||||||
* our mailbox.
|
|
||||||
*/
|
|
||||||
MailboxUpdate getLocalUpdate(Transaction txn, ContactId c)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the latest {@link MailboxUpdate} received from the given
|
|
||||||
* contact, or null if no update has been received.
|
|
||||||
* <p>
|
|
||||||
* If the contact has a mailbox then the update will be a
|
|
||||||
* {@link MailboxUpdateWithMailbox} containing the
|
|
||||||
* {@link MailboxProperties} we should use for communicating with the
|
|
||||||
* contact's mailbox.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
MailboxUpdate getRemoteUpdate(Transaction txn, ContactId c)
|
|
||||||
throws DbException;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxUpdateWithMailbox extends MailboxUpdate {
|
|
||||||
|
|
||||||
private final MailboxProperties properties;
|
|
||||||
|
|
||||||
public MailboxUpdateWithMailbox(List<MailboxVersion> clientSupports,
|
|
||||||
MailboxProperties properties) {
|
|
||||||
super(clientSupports, true);
|
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MailboxUpdateWithMailbox(MailboxUpdateWithMailbox o,
|
|
||||||
List<MailboxVersion> newClientSupports) {
|
|
||||||
this(newClientSupports, o.getMailboxProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
public MailboxProperties getMailboxProperties() {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxVersion implements Comparable<MailboxVersion> {
|
|
||||||
|
|
||||||
private final int major;
|
|
||||||
private final int minor;
|
|
||||||
|
|
||||||
public MailboxVersion(int major, int minor) {
|
|
||||||
this.major = major;
|
|
||||||
this.minor = minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMajor() {
|
|
||||||
return major;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMinor() {
|
|
||||||
return minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o instanceof MailboxVersion) {
|
|
||||||
MailboxVersion v = (MailboxVersion) o;
|
|
||||||
return major == v.major && minor == v.minor;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(MailboxVersion v) {
|
|
||||||
int c = major - v.major;
|
|
||||||
if (c != 0) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
return minor - v.minor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast by {@link MailboxSettingsManager} when
|
|
||||||
* recording a connection failure for own Mailbox
|
|
||||||
* that has persistent for long enough for the mailbox owner to become active
|
|
||||||
* and fix the problem with the mailbox.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class MailboxProblemEvent extends Event {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast by {@link MailboxSettingsManager} when
|
|
||||||
* recording the connection status of own Mailbox.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class OwnMailboxConnectionStatusEvent extends Event {
|
|
||||||
|
|
||||||
private final MailboxStatus status;
|
|
||||||
|
|
||||||
public OwnMailboxConnectionStatusEvent(MailboxStatus status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MailboxStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox.event;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An event that is broadcast when {@link MailboxUpdate} are received
|
|
||||||
* from a contact.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class RemoteMailboxUpdateEvent extends Event {
|
|
||||||
|
|
||||||
private final ContactId contactId;
|
|
||||||
private final MailboxUpdate mailboxUpdate;
|
|
||||||
|
|
||||||
public RemoteMailboxUpdateEvent(ContactId contactId,
|
|
||||||
MailboxUpdate mailboxUpdate) {
|
|
||||||
this.contactId = contactId;
|
|
||||||
this.mailboxUpdate = mailboxUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContactId getContact() {
|
|
||||||
return contactId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MailboxUpdate getMailboxUpdate() {
|
|
||||||
return mailboxUpdate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
package org.briarproject.bramble.api.plugin;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
|
||||||
public interface TorConstants {
|
public interface TorConstants {
|
||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
||||||
|
|
||||||
// Transport properties
|
// Transport properties
|
||||||
|
String PROP_ONION_V2 = "onion";
|
||||||
String PROP_ONION_V3 = "onion3";
|
String PROP_ONION_V3 = "onion3";
|
||||||
|
|
||||||
int DEFAULT_SOCKS_PORT = 59050;
|
int DEFAULT_SOCKS_PORT = 59050;
|
||||||
int DEFAULT_CONTROL_PORT = 59051;
|
int DEFAULT_CONTROL_PORT = 59051;
|
||||||
|
|
||||||
int CONNECT_TO_PROXY_TIMEOUT = (int) SECONDS.toMillis(5);
|
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||||
int EXTRA_CONNECT_TIMEOUT = (int) SECONDS.toMillis(120);
|
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
|
||||||
int EXTRA_SOCKET_TIMEOUT = (int) SECONDS.toMillis(30);
|
|
||||||
|
|
||||||
// Local settings (not shared with contacts)
|
// Local settings (not shared with contacts)
|
||||||
String PREF_TOR_NETWORK = "network2";
|
String PREF_TOR_NETWORK = "network2";
|
||||||
String PREF_TOR_PORT = "port";
|
String PREF_TOR_PORT = "port";
|
||||||
String PREF_TOR_MOBILE = "useMobileData";
|
String PREF_TOR_MOBILE = "useMobileData";
|
||||||
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
|
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
|
||||||
|
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
|
||||||
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
|
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
|
||||||
|
String HS_V3_CREATED = "onionPrivKey3Created";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long to publish a v3 hidden service before retiring the v2 service.
|
||||||
|
*/
|
||||||
|
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
|
||||||
|
|
||||||
// Values for PREF_TOR_NETWORK
|
// Values for PREF_TOR_NETWORK
|
||||||
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
||||||
|
|||||||
@@ -12,6 +12,4 @@ public interface RecordWriter {
|
|||||||
void flush() throws IOException;
|
void flush() throws IOException;
|
||||||
|
|
||||||
void close() throws IOException;
|
void close() throws IOException;
|
||||||
|
|
||||||
long getBytesWritten();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,4 @@ public interface SettingsManager {
|
|||||||
* namespace.
|
* namespace.
|
||||||
*/
|
*/
|
||||||
void mergeSettings(Settings s, String namespace) throws DbException;
|
void mergeSettings(Settings s, String namespace) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges the given settings with any existing settings in the given
|
|
||||||
* namespace.
|
|
||||||
*/
|
|
||||||
void mergeSettings(Transaction txn, Settings s, String namespace)
|
|
||||||
throws DbException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,9 @@ public class GroupId extends UniqueId {
|
|||||||
public GroupId(byte[] id) {
|
public GroupId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof GroupId && super.equals(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,9 @@ public class MessageId extends UniqueId {
|
|||||||
public MessageId(byte[] id) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof MessageId && super.equals(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container for holding the IDs of messages sent and acked during an
|
|
||||||
* outgoing {@link SyncSession}, so they can be recorded in the DB as sent
|
|
||||||
* or acked at some later time.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
public class OutgoingSessionRecord {
|
|
||||||
|
|
||||||
private final Collection<MessageId> ackedIds = new CopyOnWriteArrayList<>();
|
|
||||||
private final Collection<MessageId> sentIds = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
public void onAckSent(Collection<MessageId> acked) {
|
|
||||||
ackedIds.addAll(acked);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessageSent(MessageId sent) {
|
|
||||||
sentIds.add(sent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<MessageId> getAckedIds() {
|
|
||||||
return ackedIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<MessageId> getSentIds() {
|
|
||||||
return sentIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,4 @@ public interface SyncRecordWriter {
|
|||||||
void writePriority(Priority p) throws IOException;
|
void writePriority(Priority p) throws IOException;
|
||||||
|
|
||||||
void flush() throws IOException;
|
void flush() throws IOException;
|
||||||
|
|
||||||
long getBytesWritten();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,30 +12,12 @@ import javax.annotation.Nullable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncSessionFactory {
|
public interface SyncSessionFactory {
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a session for receiving data from a contact.
|
|
||||||
*/
|
|
||||||
SyncSession createIncomingSession(ContactId c, InputStream in,
|
SyncSession createIncomingSession(ContactId c, InputStream in,
|
||||||
PriorityHandler handler);
|
PriorityHandler handler);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a session for sending data to a contact over a simplex transport.
|
|
||||||
*
|
|
||||||
* @param eager True if messages should be sent eagerly, ie regardless of
|
|
||||||
* whether they're due for retransmission.
|
|
||||||
*/
|
|
||||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
||||||
long maxLatency, boolean eager, StreamWriter streamWriter);
|
long maxLatency, boolean eager, StreamWriter streamWriter);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a session for sending data to a contact via a mailbox. The IDs
|
|
||||||
* of any messages sent or acked will be added to the given
|
|
||||||
* {@link OutgoingSessionRecord}.
|
|
||||||
*/
|
|
||||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
|
||||||
long maxLatency, StreamWriter streamWriter,
|
|
||||||
OutgoingSessionRecord sessionRecord);
|
|
||||||
|
|
||||||
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
||||||
long maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
long maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||||
@Nullable Priority priority);
|
@Nullable Priority priority);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.sync.event;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@@ -16,19 +15,12 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class GroupVisibilityUpdatedEvent extends Event {
|
public class GroupVisibilityUpdatedEvent extends Event {
|
||||||
|
|
||||||
private final Visibility visibility;
|
|
||||||
private final Collection<ContactId> affected;
|
private final Collection<ContactId> affected;
|
||||||
|
|
||||||
public GroupVisibilityUpdatedEvent(Visibility visibility,
|
public GroupVisibilityUpdatedEvent(Collection<ContactId> affected) {
|
||||||
Collection<ContactId> affected) {
|
|
||||||
this.visibility = visibility;
|
|
||||||
this.affected = affected;
|
this.affected = affected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Visibility getVisibility() {
|
|
||||||
return visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contacts affected by the update.
|
* Returns the contacts affected by the update.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
package org.briarproject.bramble.api.system;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -17,8 +16,6 @@ public interface TaskScheduler {
|
|||||||
* <p>
|
* <p>
|
||||||
* If the platform supports wake locks, a wake lock will be held while
|
* If the platform supports wake locks, a wake lock will be held while
|
||||||
* submitting and running the task.
|
* submitting and running the task.
|
||||||
*
|
|
||||||
* @return A {@link Cancellable} for cancelling the task.
|
|
||||||
*/
|
*/
|
||||||
Cancellable schedule(Runnable task, Executor executor, long delay,
|
Cancellable schedule(Runnable task, Executor executor, long delay,
|
||||||
TimeUnit unit);
|
TimeUnit unit);
|
||||||
@@ -30,11 +27,17 @@ public interface TaskScheduler {
|
|||||||
* <p>
|
* <p>
|
||||||
* If the platform supports wake locks, a wake lock will be held while
|
* If the platform supports wake locks, a wake lock will be held while
|
||||||
* submitting and running the task.
|
* submitting and running the task.
|
||||||
*
|
|
||||||
* @return A {@link Cancellable} for cancelling all future executions of
|
|
||||||
* the task.
|
|
||||||
*/
|
*/
|
||||||
Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
|
Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
|
||||||
long delay, long interval, TimeUnit unit);
|
long delay, long interval, TimeUnit unit);
|
||||||
|
|
||||||
|
interface Cancellable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the task if it has not already started running. If the task
|
||||||
|
* is {@link #scheduleWithFixedDelay(Runnable, Executor, long, long,
|
||||||
|
* TimeUnit) periodic}, all future executions of the task are cancelled.
|
||||||
|
*/
|
||||||
|
void cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,25 +113,9 @@ public interface KeyManager {
|
|||||||
/**
|
/**
|
||||||
* Looks up the given tag and returns a {@link StreamContext} for reading
|
* Looks up the given tag and returns a {@link StreamContext} for reading
|
||||||
* from the corresponding stream, or null if an error occurs or the tag was
|
* from the corresponding stream, or null if an error occurs or the tag was
|
||||||
* unexpected. Marks the tag as recognised and updates the reordering
|
* unexpected.
|
||||||
* window.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
StreamContext getStreamContext(TransportId t, byte[] tag)
|
StreamContext getStreamContext(TransportId t, byte[] tag)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up the given tag and returns a {@link StreamContext} for reading
|
|
||||||
* from the corresponding stream, or null if an error occurs or the tag was
|
|
||||||
* unexpected. Only returns the StreamContext; does not mark the tag as
|
|
||||||
* recognised.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
StreamContext getStreamContextOnly(TransportId t, byte[] tag)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the tag as recognised and updates the reordering window.
|
|
||||||
*/
|
|
||||||
void markTagAsRecognised(TransportId t, byte[] tag) throws DbException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class IoUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void delete(File f) {
|
private static void delete(File f) {
|
||||||
if (!f.delete() && LOG.isLoggable(WARNING))
|
if (!f.delete() && LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Could not delete " + f.getAbsolutePath());
|
LOG.warning("Could not delete " + f.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class NetworkUtils {
|
|||||||
// Despite what the docs say, the return value can be null
|
// Despite what the docs say, the return value can be null
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
return ifaces == null ? emptyList() : list(ifaces);
|
return ifaces == null ? emptyList() : list(ifaces);
|
||||||
} catch (SocketException | NullPointerException e) {
|
} catch (SocketException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
return emptyList();
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_SERVER_TOO_OLD;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxHelper.getHighestCommonMajorVersion;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class MailboxHelperTest {
|
|
||||||
|
|
||||||
private final Random random = new Random();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetHighestCommonMajorVersion() {
|
|
||||||
assertEquals(2, getHighestCommonMajorVersion(v(2), v(2)));
|
|
||||||
assertEquals(2, getHighestCommonMajorVersion(v(1, 2), v(2, 3, 4)));
|
|
||||||
assertEquals(2, getHighestCommonMajorVersion(v(2, 3, 4), v(2)));
|
|
||||||
assertEquals(2, getHighestCommonMajorVersion(v(2), v(2, 3, 4)));
|
|
||||||
|
|
||||||
assertEquals(API_CLIENT_TOO_OLD,
|
|
||||||
getHighestCommonMajorVersion(v(2), v(3, 4)));
|
|
||||||
assertEquals(API_CLIENT_TOO_OLD,
|
|
||||||
getHighestCommonMajorVersion(v(2), v(1, 3)));
|
|
||||||
assertEquals(API_SERVER_TOO_OLD,
|
|
||||||
getHighestCommonMajorVersion(v(3, 4, 5), v(2)));
|
|
||||||
assertEquals(API_SERVER_TOO_OLD,
|
|
||||||
getHighestCommonMajorVersion(v(1, 3), v(2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MailboxVersion> v(int... ints) {
|
|
||||||
List<MailboxVersion> versions = new ArrayList<>(ints.length);
|
|
||||||
for (int v : ints) {
|
|
||||||
// minor versions should not matter
|
|
||||||
versions.add(new MailboxVersion(v, random.nextInt(42)));
|
|
||||||
}
|
|
||||||
return versions;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,12 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
import org.jmock.lib.concurrent.Synchroniser;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
public abstract class BrambleMockTestCase extends BrambleTestCase {
|
public abstract class BrambleMockTestCase extends BrambleTestCase {
|
||||||
|
|
||||||
protected final Mockery context = new Mockery();
|
protected final Mockery context = new Mockery();
|
||||||
|
|
||||||
public BrambleMockTestCase() {
|
|
||||||
context.setThreadingPolicy(new Synchroniser());
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void checkExpectations() {
|
public void checkExpectations() {
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
|
|||||||
@@ -1,46 +1,17 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
|
|
||||||
import java.lang.Thread.UncaughtExceptionHandler;
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
public abstract class BrambleTestCase {
|
public abstract class BrambleTestCase {
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(BrambleTestCase.class.getName());
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected volatile Throwable exceptionInBackgroundThread = null;
|
|
||||||
|
|
||||||
public BrambleTestCase() {
|
public BrambleTestCase() {
|
||||||
// Ensure exceptions thrown on worker threads cause tests to fail
|
// Ensure exceptions thrown on worker threads cause tests to fail
|
||||||
UncaughtExceptionHandler fail = (thread, throwable) -> {
|
UncaughtExceptionHandler fail = (thread, throwable) -> {
|
||||||
LOG.log(WARNING, "Caught unhandled exception", throwable);
|
throwable.printStackTrace();
|
||||||
exceptionInBackgroundThread = throwable;
|
fail();
|
||||||
};
|
};
|
||||||
Thread.setDefaultUncaughtExceptionHandler(fail);
|
Thread.setDefaultUncaughtExceptionHandler(fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void beforeBrambleTestCase() {
|
|
||||||
exceptionInBackgroundThread = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void afterBrambleTestCase() {
|
|
||||||
Throwable thrown = exceptionInBackgroundThread;
|
|
||||||
if (thrown != null) {
|
|
||||||
LOG.log(WARNING,
|
|
||||||
"Background thread has thrown an exception unexpectedly",
|
|
||||||
thrown);
|
|
||||||
throw new AssertionError(thrown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,20 +12,10 @@ import org.briarproject.bramble.api.crypto.PublicKey;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.db.CommitAction;
|
|
||||||
import org.briarproject.bramble.api.db.EventAction;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.Identity;
|
import org.briarproject.bramble.api.identity.Identity;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
@@ -35,12 +25,7 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -50,9 +35,6 @@ import java.util.Map;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
||||||
@@ -63,7 +45,6 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
|
|||||||
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
@@ -228,37 +209,6 @@ public class TestUtils {
|
|||||||
getAgreementPublicKey(), verified);
|
getAgreementPublicKey(), verified);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MailboxProperties getMailboxProperties(boolean owner,
|
|
||||||
List<MailboxVersion> serverSupports) {
|
|
||||||
String baseUrl = "http://" + getRandomString(56) + ".onion"; // TODO
|
|
||||||
MailboxAuthToken authToken = new MailboxAuthToken(getRandomId());
|
|
||||||
if (owner) {
|
|
||||||
return new MailboxProperties(baseUrl, authToken, serverSupports);
|
|
||||||
}
|
|
||||||
MailboxFolderId inboxId = new MailboxFolderId(getRandomId());
|
|
||||||
MailboxFolderId outboxId = new MailboxFolderId(getRandomId());
|
|
||||||
return new MailboxProperties(baseUrl, authToken, serverSupports,
|
|
||||||
inboxId, outboxId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeBytes(File file, byte[] bytes)
|
|
||||||
throws IOException {
|
|
||||||
FileOutputStream outputStream = new FileOutputStream(file);
|
|
||||||
//noinspection TryFinallyCanBeTryWithResources
|
|
||||||
try {
|
|
||||||
outputStream.write(bytes);
|
|
||||||
} finally {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] readBytes(File file) throws IOException {
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
FileInputStream inputStream = new FileInputStream(file);
|
|
||||||
copyAndClose(inputStream, outputStream);
|
|
||||||
return outputStream.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double getMedian(Collection<? extends Number> samples) {
|
public static double getMedian(Collection<? extends Number> samples) {
|
||||||
int size = samples.size();
|
int size = samples.size();
|
||||||
if (size == 0) throw new IllegalArgumentException();
|
if (size == 0) throw new IllegalArgumentException();
|
||||||
@@ -293,57 +243,9 @@ public class TestUtils {
|
|||||||
return Math.sqrt(getVariance(samples));
|
return Math.sqrt(getVariance(samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOptionalTestEnabled(Class<?> testClass) {
|
public static boolean isOptionalTestEnabled(Class testClass) {
|
||||||
String optionalTests = System.getenv("OPTIONAL_TESTS");
|
String optionalTests = System.getenv("OPTIONAL_TESTS");
|
||||||
return optionalTests != null &&
|
return optionalTests != null &&
|
||||||
asList(optionalTests.split(",")).contains(testClass.getName());
|
asList(optionalTests.split(",")).contains(testClass.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean mailboxUpdateEqual(@Nullable MailboxUpdate a,
|
|
||||||
@Nullable MailboxUpdate b) {
|
|
||||||
if (a == null || b == null) {
|
|
||||||
return a == b;
|
|
||||||
}
|
|
||||||
if (!a.hasMailbox() && !b.hasMailbox()) {
|
|
||||||
return a.getClientSupports().equals(b.getClientSupports());
|
|
||||||
} else if (a.hasMailbox() && b.hasMailbox()) {
|
|
||||||
MailboxUpdateWithMailbox am = (MailboxUpdateWithMailbox) a;
|
|
||||||
MailboxUpdateWithMailbox bm = (MailboxUpdateWithMailbox) b;
|
|
||||||
return am.getClientSupports().equals(bm.getClientSupports()) &&
|
|
||||||
mailboxPropertiesEqual(am.getMailboxProperties(),
|
|
||||||
bm.getMailboxProperties());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean mailboxPropertiesEqual(@Nullable MailboxProperties a,
|
|
||||||
@Nullable MailboxProperties b) {
|
|
||||||
if (a == null || b == null) {
|
|
||||||
return a == b;
|
|
||||||
}
|
|
||||||
return a.getOnion().equals(b.getOnion()) &&
|
|
||||||
a.getAuthToken().equals(b.getAuthToken()) &&
|
|
||||||
a.isOwner() == b.isOwner() &&
|
|
||||||
a.getServerSupports().equals(b.getServerSupports());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasEvent(Transaction txn,
|
|
||||||
Class<? extends Event> eventClass) {
|
|
||||||
for (CommitAction action : txn.getActions()) {
|
|
||||||
if (action instanceof EventAction) {
|
|
||||||
Event event = ((EventAction) action).getEvent();
|
|
||||||
if (eventClass.isInstance(event)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isCryptoStrengthUnlimited() {
|
|
||||||
try {
|
|
||||||
return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding")
|
|
||||||
== Integer.MAX_VALUE;
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package org.briarproject.bramble.test;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class ThreadExceptionTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test(expected = AssertionError.class)
|
|
||||||
public void testAssertionErrorMakesTestCaseFail() {
|
|
||||||
// This is what BrambleTestCase does, too:
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExceptionInThreadMakesTestCaseFail() {
|
|
||||||
Thread t = new Thread(() -> {
|
|
||||||
System.out.println("thread before exception");
|
|
||||||
throw new RuntimeException("boom");
|
|
||||||
});
|
|
||||||
|
|
||||||
t.start();
|
|
||||||
try {
|
|
||||||
t.join();
|
|
||||||
System.out.println("joined thread");
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
System.out.println("interrupted while joining thread");
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(exceptionInBackgroundThread);
|
|
||||||
exceptionInBackgroundThread = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,29 @@
|
|||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
|
'antlr:antlr:2.7.7:antlr-2.7.7.jar:88fbda4b912596b9f56e8e12e580cc954bacfb51776ecfddd3e18fc1cf56dc4c',
|
||||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
'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.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'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.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.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||||
|
'com.google.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d',
|
||||||
|
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||||
|
'com.google.guava:guava:28.1-jre:guava-28.1-jre.jar:30beb8b8527bd07c6e747e77f1a92122c2f29d57ce347461a4a55eb26e382da4',
|
||||||
|
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
|
'com.puppycrawl.tools:checkstyle:8.27:checkstyle-8.27.jar:26c81958a112ebdfc5d7b40507bbfc8f15f606fea1e55ef5675609ddb41a6053',
|
||||||
|
'commons-beanutils:commons-beanutils:1.9.4:commons-beanutils-1.9.4.jar:7d938c81789028045c08c065e94be75fc280527620d5bd62b519d5838532368a',
|
||||||
|
'commons-collections:commons-collections:3.2.2:commons-collections-3.2.2.jar:eeeae917917144a68a741d4c0dff66aa5c5c5fd85593ff217bced3fc8ca783b8',
|
||||||
|
'info.picocli:picocli:4.1.1:picocli-4.1.1.jar:9238b6af4ded57dcfedf6f7dbcf2bdf334dae2f118ea2a0bd6d05066e63ee6fc',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
|
'net.sf.saxon:Saxon-HE:9.9.1-5:Saxon-HE-9.9.1-5.jar:4b39a9e30d5b634b5e91db5f0c6937f176d4517543ababc9b5e01830b8c56620',
|
||||||
|
'org.antlr:antlr4-runtime:4.7.2:antlr4-runtime-4.7.2.jar:4c518b87d4bdff8b44cd8cbc1af816e944b62a3fe5b80b781501cf1f4759bbc4',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
|
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
||||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||||
|
'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
|
||||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
||||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ sourceCompatibility = 1.8
|
|||||||
targetCompatibility = 1.8
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
apply plugin: 'ru.vyarus.animalsniffer'
|
apply plugin: 'ru.vyarus.animalsniffer'
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
@@ -10,17 +11,12 @@ apply from: '../dagger.gradle'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-api', configuration: 'default')
|
implementation project(path: ':bramble-api', configuration: 'default')
|
||||||
implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'
|
implementation 'org.bouncycastle:bcprov-jdk15on:1.69'
|
||||||
//noinspection GradleDependency
|
|
||||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||||
implementation 'org.briarproject:jtorctl:0.4'
|
implementation 'org.briarproject:jtorctl:0.3'
|
||||||
|
|
||||||
//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"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
@@ -30,8 +26,7 @@ dependencies {
|
|||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
testImplementation "org.jmock:jmock-legacy:$jmock_version"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
|
||||||
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||||
@@ -29,8 +28,6 @@ public interface BrambleCoreEagerSingletons {
|
|||||||
|
|
||||||
void inject(LifecycleModule.EagerSingletons init);
|
void inject(LifecycleModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(MailboxModule.EagerSingletons init);
|
|
||||||
|
|
||||||
void inject(PluginModule.EagerSingletons init);
|
void inject(PluginModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(PropertiesModule.EagerSingletons init);
|
void inject(PropertiesModule.EagerSingletons init);
|
||||||
@@ -54,7 +51,6 @@ public interface BrambleCoreEagerSingletons {
|
|||||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||||
c.inject(new IdentityModule.EagerSingletons());
|
c.inject(new IdentityModule.EagerSingletons());
|
||||||
c.inject(new LifecycleModule.EagerSingletons());
|
c.inject(new LifecycleModule.EagerSingletons());
|
||||||
c.inject(new MailboxModule.EagerSingletons());
|
|
||||||
c.inject(new RendezvousModule.EagerSingletons());
|
c.inject(new RendezvousModule.EagerSingletons());
|
||||||
c.inject(new PluginModule.EagerSingletons());
|
c.inject(new PluginModule.EagerSingletons());
|
||||||
c.inject(new PropertiesModule.EagerSingletons());
|
c.inject(new PropertiesModule.EagerSingletons());
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import org.briarproject.bramble.identity.IdentityModule;
|
|||||||
import org.briarproject.bramble.io.IoModule;
|
import org.briarproject.bramble.io.IoModule;
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
@@ -44,7 +43,6 @@ import dagger.Module;
|
|||||||
IoModule.class,
|
IoModule.class,
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
MailboxModule.class,
|
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
PropertiesModule.class,
|
PropertiesModule.class,
|
||||||
RecordModule.class,
|
RecordModule.class,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.client;
|
package org.briarproject.bramble.client;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
@@ -23,12 +22,6 @@ import org.briarproject.bramble.api.db.Metadata;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
@@ -36,16 +29,13 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.util.Base32;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
@@ -56,12 +46,6 @@ import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KE
|
|||||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_COUNT;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY_AUTHTOKEN;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY_INBOXID;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY_ONION;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY_OUTBOXID;
|
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_ONION_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||||
@@ -415,69 +399,6 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
return tpMap;
|
return tpMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailboxUpdate parseAndValidateMailboxUpdate(BdfList clientSupports,
|
|
||||||
BdfList serverSupports, BdfDictionary properties)
|
|
||||||
throws FormatException {
|
|
||||||
List<MailboxVersion> clientSupportsList =
|
|
||||||
parseMailboxVersionList(clientSupports);
|
|
||||||
List<MailboxVersion> serverSupportsList =
|
|
||||||
parseMailboxVersionList(serverSupports);
|
|
||||||
|
|
||||||
// We must always learn what Mailbox API version(s) the client supports
|
|
||||||
if (clientSupports.isEmpty()) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
if (properties.isEmpty()) {
|
|
||||||
// No mailbox -- cannot claim to support any API versions!
|
|
||||||
if (!serverSupports.isEmpty()) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
return new MailboxUpdate(clientSupportsList);
|
|
||||||
}
|
|
||||||
// Mailbox must be accompanied by the Mailbox API version(s) it supports
|
|
||||||
if (serverSupports.isEmpty()) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
// Accepting more props than we need, for forward compatibility
|
|
||||||
if (properties.size() < PROP_COUNT) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
String onion = properties.getString(PROP_KEY_ONION);
|
|
||||||
checkLength(onion, PROP_ONION_LENGTH);
|
|
||||||
try {
|
|
||||||
Base32.decode(onion, true);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
byte[] authToken = properties.getRaw(PROP_KEY_AUTHTOKEN);
|
|
||||||
checkLength(authToken, UniqueId.LENGTH);
|
|
||||||
byte[] inboxId = properties.getRaw(PROP_KEY_INBOXID);
|
|
||||||
checkLength(inboxId, UniqueId.LENGTH);
|
|
||||||
byte[] outboxId = properties.getRaw(PROP_KEY_OUTBOXID);
|
|
||||||
checkLength(outboxId, UniqueId.LENGTH);
|
|
||||||
String baseUrl = "http://" + onion + ".onion"; // TODO
|
|
||||||
MailboxProperties props = new MailboxProperties(baseUrl,
|
|
||||||
new MailboxAuthToken(authToken), serverSupportsList,
|
|
||||||
new MailboxFolderId(inboxId), new MailboxFolderId(outboxId));
|
|
||||||
return new MailboxUpdateWithMailbox(clientSupportsList, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MailboxVersion> parseMailboxVersionList(BdfList bdfList)
|
|
||||||
throws FormatException {
|
|
||||||
List<MailboxVersion> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < bdfList.size(); i++) {
|
|
||||||
BdfList element = bdfList.getList(i);
|
|
||||||
if (element.size() != 2) {
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
|
||||||
element.getLong(1).intValue()));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ abstract class Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] readTag(InputStream in) throws IOException {
|
private byte[] readTag(InputStream in) throws IOException {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
read(in, tag);
|
read(in, tag);
|
||||||
return tag;
|
return tag;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||||
@@ -68,15 +67,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionReader r) {
|
TransportConnectionReader r) {
|
||||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||||
syncSessionFactory, transportPropertyManager, t, r, null));
|
syncSessionFactory, transportPropertyManager, t, r));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageIncomingConnection(TransportId t,
|
|
||||||
TransportConnectionReader r, TagController c) {
|
|
||||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, t, r, c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,16 +92,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionWriter w) {
|
TransportConnectionWriter w) {
|
||||||
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
|
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||||
syncSessionFactory, transportPropertyManager, c, t, w, null));
|
syncSessionFactory, transportPropertyManager, c, t, w));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void manageOutgoingConnection(ContactId c, TransportId t,
|
|
||||||
TransportConnectionWriter w, OutgoingSessionRecord sessionRecord) {
|
|
||||||
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
|
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
|
||||||
syncSessionFactory, transportPropertyManager, c, t, w,
|
|
||||||
sessionRecord));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package org.briarproject.bramble.connection;
|
package org.briarproject.bramble.connection;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager.TagController;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -17,8 +15,6 @@ import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@@ -27,8 +23,6 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
|
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final TransportConnectionReader reader;
|
private final TransportConnectionReader reader;
|
||||||
@Nullable
|
|
||||||
private final TagController tagController;
|
|
||||||
|
|
||||||
IncomingSimplexSyncConnection(KeyManager keyManager,
|
IncomingSimplexSyncConnection(KeyManager keyManager,
|
||||||
ConnectionRegistry connectionRegistry,
|
ConnectionRegistry connectionRegistry,
|
||||||
@@ -36,50 +30,33 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
StreamWriterFactory streamWriterFactory,
|
StreamWriterFactory streamWriterFactory,
|
||||||
SyncSessionFactory syncSessionFactory,
|
SyncSessionFactory syncSessionFactory,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
TransportId transportId,
|
TransportId transportId, TransportConnectionReader reader) {
|
||||||
TransportConnectionReader reader,
|
|
||||||
@Nullable TagController tagController) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||||
streamWriterFactory, syncSessionFactory,
|
streamWriterFactory, syncSessionFactory,
|
||||||
transportPropertyManager);
|
transportPropertyManager);
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.tagController = tagController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Read and recognise the tag
|
// Read and recognise the tag
|
||||||
byte[] tag;
|
StreamContext ctx = recogniseTag(reader, transportId);
|
||||||
StreamContext ctx;
|
|
||||||
try {
|
|
||||||
tag = readTag(reader.getInputStream());
|
|
||||||
// If we have a tag controller, defer marking the tag as recognised
|
|
||||||
if (tagController == null) {
|
|
||||||
ctx = keyManager.getStreamContext(transportId, tag);
|
|
||||||
} else {
|
|
||||||
ctx = keyManager.getStreamContextOnly(transportId, tag);
|
|
||||||
}
|
|
||||||
} catch (IOException | DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
LOG.info("Unrecognised tag");
|
LOG.info("Unrecognised tag");
|
||||||
onError();
|
onError(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContactId contactId = ctx.getContactId();
|
ContactId contactId = ctx.getContactId();
|
||||||
if (contactId == null) {
|
if (contactId == null) {
|
||||||
LOG.warning("Received rendezvous stream, expected contact");
|
LOG.warning("Received rendezvous stream, expected contact");
|
||||||
onError(tag);
|
onError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ctx.isHandshakeMode()) {
|
if (ctx.isHandshakeMode()) {
|
||||||
// TODO: Support handshake mode for contacts
|
// TODO: Support handshake mode for contacts
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
onError(tag);
|
onError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -88,33 +65,15 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
LOG.info("Ignoring priority for simplex connection");
|
LOG.info("Ignoring priority for simplex connection");
|
||||||
// Create and run the incoming session
|
// Create and run the incoming session
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
createIncomingSession(ctx, reader, handler).run();
|
||||||
// Success
|
|
||||||
markTagAsRecognisedIfRequired(false, tag);
|
|
||||||
reader.dispose(false, true);
|
reader.dispose(false, true);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
onError(tag);
|
onError(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onError() {
|
private void onError(boolean recognised) {
|
||||||
disposeOnError(reader, false);
|
disposeOnError(reader, recognised);
|
||||||
}
|
|
||||||
|
|
||||||
private void onError(byte[] tag) {
|
|
||||||
markTagAsRecognisedIfRequired(true, tag);
|
|
||||||
disposeOnError(reader, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void markTagAsRecognisedIfRequired(boolean exception, byte[] tag) {
|
|
||||||
if (tagController != null &&
|
|
||||||
tagController.shouldMarkTagAsRecognised(exception)) {
|
|
||||||
try {
|
|
||||||
keyManager.markTagAsRecognised(transportId, tag);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
|
|
||||||
import org.briarproject.bramble.api.sync.SyncSession;
|
import org.briarproject.bramble.api.sync.SyncSession;
|
||||||
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
import org.briarproject.bramble.api.sync.SyncSessionFactory;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
@@ -17,8 +16,6 @@ import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
@@ -29,8 +26,6 @@ class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final TransportConnectionWriter writer;
|
private final TransportConnectionWriter writer;
|
||||||
@Nullable
|
|
||||||
private final OutgoingSessionRecord sessionRecord;
|
|
||||||
|
|
||||||
OutgoingSimplexSyncConnection(KeyManager keyManager,
|
OutgoingSimplexSyncConnection(KeyManager keyManager,
|
||||||
ConnectionRegistry connectionRegistry,
|
ConnectionRegistry connectionRegistry,
|
||||||
@@ -39,15 +34,13 @@ class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
SyncSessionFactory syncSessionFactory,
|
SyncSessionFactory syncSessionFactory,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
ContactId contactId, TransportId transportId,
|
ContactId contactId, TransportId transportId,
|
||||||
TransportConnectionWriter writer,
|
TransportConnectionWriter writer) {
|
||||||
@Nullable OutgoingSessionRecord sessionRecord) {
|
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||||
streamWriterFactory, syncSessionFactory,
|
streamWriterFactory, syncSessionFactory,
|
||||||
transportPropertyManager);
|
transportPropertyManager);
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.sessionRecord = sessionRecord;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,16 +71,10 @@ class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||||
w.getOutputStream(), ctx);
|
w.getOutputStream(), ctx);
|
||||||
ContactId c = requireNonNull(ctx.getContactId());
|
ContactId c = requireNonNull(ctx.getContactId());
|
||||||
if (sessionRecord == null) {
|
// Use eager retransmission if the transport is lossy and cheap
|
||||||
// Use eager retransmission if the transport is lossy and cheap
|
return syncSessionFactory.createSimplexOutgoingSession(c,
|
||||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
ctx.getTransportId(), w.getMaxLatency(), w.isLossyAndCheap(),
|
||||||
ctx.getTransportId(), w.getMaxLatency(),
|
streamWriter);
|
||||||
w.isLossyAndCheap(), streamWriter);
|
|
||||||
} else {
|
|
||||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
|
||||||
ctx.getTransportId(), w.getMaxLatency(), streamWriter,
|
|
||||||
sessionRecord);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,40 +121,28 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHandshakeLink() throws DbException {
|
public String getHandshakeLink() throws DbException {
|
||||||
return db.transactionWithResult(true, this::getHandshakeLink);
|
KeyPair keyPair = db.transactionWithResult(true,
|
||||||
}
|
identityManager::getHandshakeKeys);
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHandshakeLink(Transaction txn) throws DbException {
|
|
||||||
KeyPair keyPair = identityManager.getHandshakeKeys(txn);
|
|
||||||
return pendingContactFactory.createHandshakeLink(keyPair.getPublic());
|
return pendingContactFactory.createHandshakeLink(keyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PendingContact addPendingContact(Transaction txn, String link,
|
|
||||||
String alias)
|
|
||||||
throws DbException, FormatException, GeneralSecurityException {
|
|
||||||
PendingContact p =
|
|
||||||
pendingContactFactory.createPendingContact(link, alias);
|
|
||||||
AuthorId local = identityManager.getLocalAuthor(txn).getId();
|
|
||||||
db.addPendingContact(txn, p, local);
|
|
||||||
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
|
|
||||||
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
|
|
||||||
ourKeyPair);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PendingContact addPendingContact(String link, String alias)
|
public PendingContact addPendingContact(String link, String alias)
|
||||||
throws DbException, FormatException, GeneralSecurityException {
|
throws DbException, FormatException, GeneralSecurityException {
|
||||||
|
PendingContact p =
|
||||||
|
pendingContactFactory.createPendingContact(link, alias);
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
PendingContact p = addPendingContact(txn, link, alias);
|
AuthorId local = identityManager.getLocalAuthor(txn).getId();
|
||||||
|
db.addPendingContact(txn, p, local);
|
||||||
|
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
|
||||||
|
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
|
||||||
|
ourKeyPair);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
return p;
|
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -166,14 +154,8 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
|
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return db.transactionWithResult(true, this::getPendingContacts);
|
Collection<PendingContact> pendingContacts =
|
||||||
}
|
db.transactionWithResult(true, db::getPendingContacts);
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts(
|
|
||||||
Transaction txn)
|
|
||||||
throws DbException {
|
|
||||||
Collection<PendingContact> pendingContacts = db.getPendingContacts(txn);
|
|
||||||
List<Pair<PendingContact, PendingContactState>> pairs =
|
List<Pair<PendingContact, PendingContactState>> pairs =
|
||||||
new ArrayList<>(pendingContacts.size());
|
new ArrayList<>(pendingContacts.size());
|
||||||
for (PendingContact p : pendingContacts) {
|
for (PendingContact p : pendingContacts) {
|
||||||
@@ -186,13 +168,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePendingContact(PendingContactId p) throws DbException {
|
public void removePendingContact(PendingContactId p) throws DbException {
|
||||||
db.transaction(false, txn -> removePendingContact(txn, p));
|
db.transaction(false, txn -> db.removePendingContact(txn, p));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removePendingContact(Transaction txn, PendingContactId p)
|
|
||||||
throws DbException {
|
|
||||||
db.removePendingContact(txn, p);
|
|
||||||
states.remove(p);
|
states.remove(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
|
|||||||
import org.bouncycastle.crypto.CryptoException;
|
import org.bouncycastle.crypto.CryptoException;
|
||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
||||||
import org.bouncycastle.crypto.digests.SHA3Digest;
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
@@ -23,13 +21,11 @@ import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
|
|||||||
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.Base32;
|
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.whispersystems.curve25519.Curve25519;
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
@@ -42,7 +38,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
import static java.lang.System.arraycopy;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||||
@@ -56,15 +51,13 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
|||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(CryptoComponentImpl.class.getName());
|
Logger.getLogger(CryptoComponentImpl.class.getName());
|
||||||
|
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||||
private static final byte PBKDF_FORMAT_SCRYPT = 0;
|
private static final byte PBKDF_FORMAT_SCRYPT = 0;
|
||||||
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
|
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
|
||||||
private static final byte ONION_HS_PROTOCOL_VERSION = 3;
|
|
||||||
private static final int ONION_CHECKSUM_BYTES = 2;
|
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final PasswordBasedKdf passwordBasedKdf;
|
private final PasswordBasedKdf passwordBasedKdf;
|
||||||
@@ -130,13 +123,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UniqueId generateUniqueId() {
|
|
||||||
byte[] b = new byte[UniqueId.LENGTH];
|
|
||||||
secureRandom.nextBytes(b);
|
|
||||||
return new UniqueId(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
@@ -456,21 +442,4 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public String asciiArmour(byte[] b, int lineLength) {
|
public String asciiArmour(byte[] b, int lineLength) {
|
||||||
return AsciiArmour.wrap(b, lineLength);
|
return AsciiArmour.wrap(b, lineLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String encodeOnion(byte[] publicKey) {
|
|
||||||
Digest digest = new SHA3Digest(256);
|
|
||||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
|
||||||
digest.update(label, 0, label.length);
|
|
||||||
digest.update(publicKey, 0, publicKey.length);
|
|
||||||
digest.update(ONION_HS_PROTOCOL_VERSION);
|
|
||||||
byte[] checksum = new byte[digest.getDigestSize()];
|
|
||||||
digest.doFinal(checksum, 0);
|
|
||||||
byte[] address = new byte[publicKey.length + ONION_CHECKSUM_BYTES + 1];
|
|
||||||
arraycopy(publicKey, 0, address, 0, publicKey.length);
|
|
||||||
arraycopy(checksum, 0, address, publicKey.length, ONION_CHECKSUM_BYTES);
|
|
||||||
address[address.length - 1] = ONION_HS_PROTOCOL_VERSION;
|
|
||||||
return Base32.encode(address).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,11 +163,16 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there are any acks to send to the given contact.
|
* Returns true if there are any acks or messages to send to the given
|
||||||
|
* contact over a transport with the given maximum latency.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
|
*
|
||||||
|
* @param eager True if messages that are not yet due for retransmission
|
||||||
|
* should be included
|
||||||
*/
|
*/
|
||||||
boolean containsAcksToSend(T txn, ContactId c) throws DbException;
|
boolean containsAnythingToSend(T txn, ContactId c, long maxLatency,
|
||||||
|
boolean eager) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact for the given
|
||||||
@@ -207,18 +212,6 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
boolean containsMessage(T txn, MessageId m) throws DbException;
|
boolean containsMessage(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if there are any messages to send to the given
|
|
||||||
* contact over a transport with the given maximum latency.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*
|
|
||||||
* @param eager True if messages that are not yet due for retransmission
|
|
||||||
* should be included
|
|
||||||
*/
|
|
||||||
boolean containsMessagesToSend(T txn, ContactId c, long maxLatency,
|
|
||||||
boolean eager) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given pending contact.
|
* Returns true if the database contains the given pending contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -413,12 +406,6 @@ interface Database<T> {
|
|||||||
Collection<MessageId> getMessageIds(T txn, GroupId g, Metadata query)
|
Collection<MessageId> getMessageIds(T txn, GroupId g, Metadata query)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of the given message in bytes, including the
|
|
||||||
* message header.
|
|
||||||
*/
|
|
||||||
int getMessageLength(T txn, MessageId m) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for all delivered messages in the given group.
|
* Returns the metadata for all delivered messages in the given group.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -509,8 +496,7 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of some messages that are eligible to be sent to the
|
* Returns the IDs of some messages that are eligible to be sent to the
|
||||||
* given contact. The total length of the messages including record headers
|
* given contact, up to the given total length.
|
||||||
* will be no more than the given capacity.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Unlike {@link #getUnackedMessagesToSend(Object, ContactId)} this method
|
* Unlike {@link #getUnackedMessagesToSend(Object, ContactId)} this method
|
||||||
* does not return messages that have already been sent unless they are
|
* does not return messages that have already been sent unless they are
|
||||||
@@ -518,20 +504,20 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessagesToSend(T txn, ContactId c, long capacity,
|
Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength,
|
||||||
long maxLatency) throws DbException;
|
long maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of all messages that are eligible to be sent to the
|
* Returns the IDs of all messages that are eligible to be sent to the
|
||||||
* given contact.
|
* given contact, together with their raw lengths.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Unlike {@link #getMessagesToSend(Object, ContactId, long, long)} this
|
* Unlike {@link #getMessagesToSend(Object, ContactId, int, long)} this
|
||||||
* method may return messages that have already been sent and are not yet
|
* method may return messages that have already been sent and are not yet
|
||||||
* due for retransmission.
|
* due for retransmission.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getUnackedMessagesToSend(T txn, ContactId c)
|
Map<MessageId, Integer> getUnackedMessagesToSend(T txn, ContactId c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -587,16 +573,13 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||||
* message is due to be sent to the given contact over a transport with
|
* message is due to be sent to the given contact. The returned value may
|
||||||
* the given latency.
|
* be zero if a message is due to be sent immediately, or Long.MAX_VALUE
|
||||||
* <p>
|
* if no messages are scheduled to be sent.
|
||||||
* The returned value may be zero if a message is due to be sent
|
|
||||||
* immediately, or Long.MAX_VALUE if no messages are scheduled to be sent.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
long getNextSendTime(T txn, ContactId c, long maxLatency)
|
long getNextSendTime(T txn, ContactId c) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pending contact with the given ID.
|
* Returns the pending contact with the given ID.
|
||||||
@@ -615,14 +598,13 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of some messages that are eligible to be sent to the
|
* Returns the IDs of some messages that are eligible to be sent to the
|
||||||
* given contact and have been requested by the contact. The total length
|
* given contact and have been requested by the contact, up to the given
|
||||||
* of the messages including record headers will be no more than the given
|
* total length.
|
||||||
* capacity.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
|
Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
|
||||||
long capacity, long maxLatency) throws DbException;
|
int maxLength, long maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all settings in the given namespace.
|
* Returns all settings in the given namespace.
|
||||||
@@ -775,14 +757,6 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
|
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the transmission count, expiry time and max latency of all
|
|
||||||
* messages that are eligible to be sent to the given contact. This includes
|
|
||||||
* messages that have already been sent and are not yet due for
|
|
||||||
* retransmission.
|
|
||||||
*/
|
|
||||||
void resetUnackedMessagesToSend(T txn, ContactId c) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cleanup timer duration for the given message. This does not
|
* Sets the cleanup timer duration for the given message. This does not
|
||||||
* start the message's cleanup timer.
|
* start the message's cleanup timer.
|
||||||
@@ -867,13 +841,11 @@ interface Database<T> {
|
|||||||
void stopCleanupTimer(T txn, MessageId m) throws DbException;
|
void stopCleanupTimer(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the transmission count, expiry time and max latency of the given
|
* Updates the transmission count, expiry time and estimated time of arrival
|
||||||
* message with respect to the given contact.
|
* of the given message with respect to the given contact, using the latency
|
||||||
*
|
* of the transport over which it was sent.
|
||||||
* @param maxLatency latency of the transport over which the message was
|
|
||||||
* sent.
|
|
||||||
*/
|
*/
|
||||||
void updateRetransmissionData(T txn, ContactId c, MessageId m,
|
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m,
|
||||||
long maxLatency) throws DbException;
|
long maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import org.briarproject.bramble.api.transport.TransportKeys;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -86,7 +87,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
@@ -342,12 +342,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsAcksToSend(Transaction transaction, ContactId c)
|
public boolean containsAnythingToSend(Transaction transaction, ContactId c,
|
||||||
throws DbException {
|
long maxLatency, boolean eager) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
return db.containsAcksToSend(txn, c);
|
return db.containsAnythingToSend(txn, c, maxLatency, eager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -373,15 +373,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.containsIdentity(txn, a);
|
return db.containsIdentity(txn, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsMessagesToSend(Transaction transaction, ContactId c,
|
|
||||||
long maxLatency, boolean eager) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
return db.containsMessagesToSend(txn, c, maxLatency, eager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsPendingContact(Transaction transaction,
|
public boolean containsPendingContact(Transaction transaction,
|
||||||
PendingContactId p) throws DbException {
|
PendingContactId p) throws DbException {
|
||||||
@@ -433,27 +424,53 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Collection<Message> generateBatch(Transaction transaction,
|
public Collection<Message> generateBatch(Transaction transaction,
|
||||||
ContactId c, long capacity, long maxLatency) throws DbException {
|
ContactId c, int maxLength, long maxLatency) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
Collection<MessageId> ids =
|
Collection<MessageId> ids =
|
||||||
db.getMessagesToSend(txn, c, capacity, maxLatency);
|
db.getMessagesToSend(txn, c, maxLength, maxLatency);
|
||||||
if (ids.isEmpty()) return null;
|
|
||||||
long totalLength = 0;
|
long totalLength = 0;
|
||||||
List<Message> messages = new ArrayList<>(ids.size());
|
List<Message> messages = new ArrayList<>(ids.size());
|
||||||
for (MessageId m : ids) {
|
for (MessageId m : ids) {
|
||||||
Message message = db.getMessage(txn, m);
|
Message message = db.getMessage(txn, m);
|
||||||
totalLength += message.getRawLength();
|
totalLength += message.getRawLength();
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
db.updateRetransmissionData(txn, c, m, maxLatency);
|
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||||
}
|
}
|
||||||
|
if (ids.isEmpty()) return null;
|
||||||
db.lowerRequestedFlag(txn, c, ids);
|
db.lowerRequestedFlag(txn, c, ids);
|
||||||
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Message> generateBatch(Transaction transaction,
|
||||||
|
ContactId c, Collection<MessageId> ids, long maxLatency)
|
||||||
|
throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsContact(txn, c))
|
||||||
|
throw new NoSuchContactException();
|
||||||
|
long totalLength = 0;
|
||||||
|
List<Message> messages = new ArrayList<>(ids.size());
|
||||||
|
List<MessageId> sentIds = new ArrayList<>(ids.size());
|
||||||
|
for (MessageId m : ids) {
|
||||||
|
if (db.containsVisibleMessage(txn, c, m)) {
|
||||||
|
Message message = db.getMessage(txn, m);
|
||||||
|
totalLength += message.getRawLength();
|
||||||
|
messages.add(message);
|
||||||
|
sentIds.add(m);
|
||||||
|
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messages.isEmpty()) return messages;
|
||||||
|
db.lowerRequestedFlag(txn, c, sentIds);
|
||||||
|
transaction.attach(new MessagesSentEvent(c, sentIds, totalLength));
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Offer generateOffer(Transaction transaction, ContactId c,
|
public Offer generateOffer(Transaction transaction, ContactId c,
|
||||||
@@ -466,7 +483,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.getMessagesToOffer(txn, c, maxMessages, maxLatency);
|
db.getMessagesToOffer(txn, c, maxMessages, maxLatency);
|
||||||
if (ids.isEmpty()) return null;
|
if (ids.isEmpty()) return null;
|
||||||
for (MessageId m : ids)
|
for (MessageId m : ids)
|
||||||
db.updateRetransmissionData(txn, c, m, maxLatency);
|
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||||
return new Offer(ids);
|
return new Offer(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,22 +505,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Collection<Message> generateRequestedBatch(Transaction transaction,
|
public Collection<Message> generateRequestedBatch(Transaction transaction,
|
||||||
ContactId c, long capacity, long maxLatency) throws DbException {
|
ContactId c, int maxLength, long maxLatency) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
Collection<MessageId> ids =
|
Collection<MessageId> ids =
|
||||||
db.getRequestedMessagesToSend(txn, c, capacity, maxLatency);
|
db.getRequestedMessagesToSend(txn, c, maxLength, maxLatency);
|
||||||
if (ids.isEmpty()) return null;
|
|
||||||
long totalLength = 0;
|
long totalLength = 0;
|
||||||
List<Message> messages = new ArrayList<>(ids.size());
|
List<Message> messages = new ArrayList<>(ids.size());
|
||||||
for (MessageId m : ids) {
|
for (MessageId m : ids) {
|
||||||
Message message = db.getMessage(txn, m);
|
Message message = db.getMessage(txn, m);
|
||||||
totalLength += message.getRawLength();
|
totalLength += message.getRawLength();
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
db.updateRetransmissionData(txn, c, m, maxLatency);
|
db.updateExpiryTimeAndEta(txn, c, m, maxLatency);
|
||||||
}
|
}
|
||||||
|
if (ids.isEmpty()) return null;
|
||||||
db.lowerRequestedFlag(txn, c, ids);
|
db.lowerRequestedFlag(txn, c, ids);
|
||||||
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
transaction.attach(new MessagesSentEvent(c, ids, totalLength));
|
||||||
return messages;
|
return messages;
|
||||||
@@ -618,24 +635,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getMessageIds(txn, g, query);
|
return db.getMessageIds(txn, g, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<MessageId> getMessagesToAck(Transaction transaction,
|
|
||||||
ContactId c, int maxMessages) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
return db.getMessagesToAck(txn, c, maxMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<MessageId> getMessagesToSend(Transaction transaction,
|
|
||||||
ContactId c, long capacity, long maxLatency) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
return db.getMessagesToSend(txn, c, capacity, maxLatency);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -741,44 +740,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public Message getMessageToSend(Transaction transaction, ContactId c,
|
public Map<MessageId, Integer> getUnackedMessagesToSend(
|
||||||
MessageId m, long maxLatency, boolean markAsSent)
|
Transaction transaction,
|
||||||
throws DbException {
|
ContactId c) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
if (!db.containsVisibleMessage(txn, c, m)) return null;
|
|
||||||
Message message = db.getMessage(txn, m);
|
|
||||||
if (markAsSent) {
|
|
||||||
db.updateRetransmissionData(txn, c, m, maxLatency);
|
|
||||||
db.lowerRequestedFlag(txn, c, singletonList(m));
|
|
||||||
transaction.attach(new MessagesSentEvent(c, singletonList(m),
|
|
||||||
message.getRawLength()));
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<MessageId> getUnackedMessagesToSend(
|
|
||||||
Transaction transaction, ContactId c) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
return db.getUnackedMessagesToSend(txn, c);
|
return db.getUnackedMessagesToSend(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetUnackedMessagesToSend(Transaction transaction, ContactId c)
|
|
||||||
throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
db.resetUnackedMessagesToSend(txn, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getUnackedMessageBytesToSend(Transaction transaction,
|
public long getUnackedMessageBytesToSend(Transaction transaction,
|
||||||
ContactId c) throws DbException {
|
ContactId c) throws DbException {
|
||||||
@@ -814,10 +785,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNextSendTime(Transaction transaction, ContactId c,
|
public long getNextSendTime(Transaction transaction, ContactId c)
|
||||||
long maxLatency) throws DbException {
|
throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
return db.getNextSendTime(txn, c, maxLatency);
|
return db.getNextSendTime(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1025,8 +996,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.getGroupVisibility(txn, id).keySet();
|
db.getGroupVisibility(txn, id).keySet();
|
||||||
db.removeGroup(txn, id);
|
db.removeGroup(txn, id);
|
||||||
transaction.attach(new GroupRemovedEvent(g));
|
transaction.attach(new GroupRemovedEvent(g));
|
||||||
transaction.attach(new GroupVisibilityUpdatedEvent(INVISIBLE,
|
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
||||||
affected));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1090,20 +1060,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.removeTransportKeys(txn, t, k);
|
db.removeTransportKeys(txn, t, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAckSent(Transaction transaction, ContactId c,
|
|
||||||
Collection<MessageId> acked) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
List<MessageId> visible = new ArrayList<>(acked.size());
|
|
||||||
for (MessageId m : acked) {
|
|
||||||
if (db.containsVisibleMessage(txn, c, m)) visible.add(m);
|
|
||||||
}
|
|
||||||
db.lowerAckFlag(txn, c, visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCleanupTimerDuration(Transaction transaction, MessageId m,
|
public void setCleanupTimerDuration(Transaction transaction, MessageId m,
|
||||||
long duration) throws DbException {
|
long duration) throws DbException {
|
||||||
@@ -1150,8 +1106,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
if (old == INVISIBLE) db.addGroupVisibility(txn, c, g, v == SHARED);
|
if (old == INVISIBLE) db.addGroupVisibility(txn, c, g, v == SHARED);
|
||||||
else if (v == INVISIBLE) db.removeGroupVisibility(txn, c, g);
|
else if (v == INVISIBLE) db.removeGroupVisibility(txn, c, g);
|
||||||
else db.setGroupVisibility(txn, c, g, v == SHARED);
|
else db.setGroupVisibility(txn, c, g, v == SHARED);
|
||||||
List<ContactId> affected = singletonList(c);
|
List<ContactId> affected = Collections.singletonList(c);
|
||||||
transaction.attach(new GroupVisibilityUpdatedEvent(v, affected));
|
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1198,28 +1154,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
transaction.attach(new MessageStateChangedEvent(m, false, state));
|
transaction.attach(new MessageStateChangedEvent(m, false, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setMessagesSent(Transaction transaction, ContactId c,
|
|
||||||
Collection<MessageId> sent, long maxLatency) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
long totalLength = 0;
|
|
||||||
List<MessageId> visible = new ArrayList<>(sent.size());
|
|
||||||
for (MessageId m : sent) {
|
|
||||||
if (db.containsVisibleMessage(txn, c, m)) {
|
|
||||||
visible.add(m);
|
|
||||||
totalLength += db.getMessageLength(txn, m);
|
|
||||||
db.updateRetransmissionData(txn, c, m, maxLatency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.lowerRequestedFlag(txn, c, visible);
|
|
||||||
if (!visible.isEmpty()) {
|
|
||||||
transaction.attach(new MessagesSentEvent(c, visible, totalLength));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMessageDependencies(Transaction transaction,
|
public void addMessageDependencies(Transaction transaction,
|
||||||
Message dependent, Collection<MessageId> dependencies)
|
Message dependent, Collection<MessageId> dependencies)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.db;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
|
||||||
interface DatabaseConstants {
|
interface DatabaseConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,6 +25,19 @@ interface DatabaseConstants {
|
|||||||
*/
|
*/
|
||||||
String SCHEMA_VERSION_KEY = "schemaVersion";
|
String SCHEMA_VERSION_KEY = "schemaVersion";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Settings} key under which the time of the last database
|
||||||
|
* compaction is stored.
|
||||||
|
*/
|
||||||
|
String LAST_COMPACTED_KEY = "lastCompacted";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum time between database compactions in milliseconds. When the
|
||||||
|
* database is opened it will be compacted if more than this amount of time
|
||||||
|
* has passed since the last compaction.
|
||||||
|
*/
|
||||||
|
long MAX_COMPACTION_INTERVAL_MS = DAYS.toMillis(30);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link Settings} key under which the flag is stored indicating
|
* The {@link Settings} key under which the flag is stored indicating
|
||||||
* whether the database is marked as dirty.
|
* whether the database is marked as dirty.
|
||||||
|
|||||||
@@ -85,17 +85,12 @@ class H2Database extends JdbcDatabase {
|
|||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
// H2 will close the database when the last connection closes
|
// H2 will close the database when the last connection closes
|
||||||
Connection c = null;
|
Connection c = null;
|
||||||
Statement s = null;
|
|
||||||
try {
|
try {
|
||||||
c = createConnection();
|
c = createConnection();
|
||||||
closeAllConnections();
|
super.closeAllConnections();
|
||||||
setDirty(c, false);
|
setDirty(c, false);
|
||||||
s = c.createStatement();
|
|
||||||
s.execute("SHUTDOWN COMPACT");
|
|
||||||
s.close();
|
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
|
||||||
tryToClose(c, LOG, WARNING);
|
tryToClose(c, LOG, WARNING);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
Connection c = null;
|
Connection c = null;
|
||||||
Statement s = null;
|
Statement s = null;
|
||||||
try {
|
try {
|
||||||
closeAllConnections();
|
super.closeAllConnections();
|
||||||
c = createConnection();
|
c = createConnection();
|
||||||
setDirty(c, false);
|
setDirty(c, false);
|
||||||
s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.executeQuery("SHUTDOWN COMPACT");
|
s.executeQuery("SHUTDOWN");
|
||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -106,7 +106,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
Connection c = null;
|
Connection c = null;
|
||||||
Statement s = null;
|
Statement s = null;
|
||||||
try {
|
try {
|
||||||
closeAllConnections();
|
super.closeAllConnections();
|
||||||
c = createConnection();
|
c = createConnection();
|
||||||
s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.executeQuery("SHUTDOWN COMPACT");
|
s.executeQuery("SHUTDOWN COMPACT");
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -69,14 +70,12 @@ import static java.sql.Types.BOOLEAN;
|
|||||||
import static java.sql.Types.INTEGER;
|
import static java.sql.Types.INTEGER;
|
||||||
import static java.sql.Types.VARCHAR;
|
import static java.sql.Types.VARCHAR;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.FINE;
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.bramble.api.record.Record.RECORD_HEADER_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
@@ -86,6 +85,8 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
|||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.DIRTY_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.DIRTY_KEY;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||||
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||||
@@ -101,12 +102,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
|||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
static final int CODE_SCHEMA_VERSION = 50;
|
static final int CODE_SCHEMA_VERSION = 49;
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of idle connections to keep open.
|
|
||||||
*/
|
|
||||||
private static final int MAX_CONNECTION_POOL_SIZE = 1;
|
|
||||||
|
|
||||||
// Time period offsets for incoming transport keys
|
// Time period offsets for incoming transport keys
|
||||||
private static final int OFFSET_PREV = -1;
|
private static final int OFFSET_PREV = -1;
|
||||||
@@ -256,7 +252,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " requested BOOLEAN NOT NULL,"
|
+ " requested BOOLEAN NOT NULL,"
|
||||||
+ " expiry BIGINT NOT NULL,"
|
+ " expiry BIGINT NOT NULL,"
|
||||||
+ " txCount INT NOT NULL,"
|
+ " txCount INT NOT NULL,"
|
||||||
+ " maxLatency BIGINT," // Null if latency was reset
|
+ " eta BIGINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, contactId),"
|
+ " PRIMARY KEY (messageId, contactId),"
|
||||||
+ " FOREIGN KEY (messageId)"
|
+ " FOREIGN KEY (messageId)"
|
||||||
+ " REFERENCES messages (messageId)"
|
+ " REFERENCES messages (messageId)"
|
||||||
@@ -369,7 +365,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||||
|
|
||||||
@GuardedBy("connectionsLock")
|
@GuardedBy("connectionsLock")
|
||||||
private final LinkedList<Connection> connectionPool = new LinkedList<>();
|
private final LinkedList<Connection> connections = new LinkedList<>();
|
||||||
|
|
||||||
@GuardedBy("connectionsLock")
|
@GuardedBy("connectionsLock")
|
||||||
private int openConnections = 0;
|
private int openConnections = 0;
|
||||||
@@ -382,7 +378,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
throws DbException, SQLException;
|
throws DbException, SQLException;
|
||||||
|
|
||||||
// Used exclusively during open to compact the database after schema
|
// Used exclusively during open to compact the database after schema
|
||||||
// migrations or if the database was not shut down cleanly
|
// migrations or after DatabaseConstants#MAX_COMPACTION_INTERVAL_MS has
|
||||||
|
// elapsed
|
||||||
protected abstract void compactAndClose() throws DbException;
|
protected abstract void compactAndClose() throws DbException;
|
||||||
|
|
||||||
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
||||||
@@ -408,8 +405,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (reopen) {
|
if (reopen) {
|
||||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
wasDirtyOnInitialisation = isDirty(s);
|
wasDirtyOnInitialisation = isDirty(s);
|
||||||
boolean migrated = migrateSchema(txn, s, listener);
|
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
||||||
compact = wasDirtyOnInitialisation || migrated;
|
|
||||||
} else {
|
} else {
|
||||||
wasDirtyOnInitialisation = false;
|
wasDirtyOnInitialisation = false;
|
||||||
createTables(txn);
|
createTables(txn);
|
||||||
@@ -433,11 +429,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
compactAndClose();
|
compactAndClose();
|
||||||
logDuration(LOG, "Compacting database", start);
|
logDuration(LOG, "Compacting database", start);
|
||||||
// Allow the next transaction to reopen the DB
|
// Allow the next transaction to reopen the DB
|
||||||
connectionsLock.lock();
|
synchronized (connectionsLock) {
|
||||||
try {
|
|
||||||
closed = false;
|
closed = false;
|
||||||
} finally {
|
}
|
||||||
connectionsLock.unlock();
|
txn = startTransaction();
|
||||||
|
try {
|
||||||
|
storeLastCompacted(txn);
|
||||||
|
commitTransaction(txn);
|
||||||
|
} catch (DbException e) {
|
||||||
|
abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,11 +499,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
new Migration45_46(),
|
new Migration45_46(),
|
||||||
new Migration46_47(dbTypes),
|
new Migration46_47(dbTypes),
|
||||||
new Migration47_48(),
|
new Migration47_48(),
|
||||||
new Migration48_49(),
|
new Migration48_49()
|
||||||
new Migration49_50()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCompactionDue(Settings s) {
|
||||||
|
long lastCompacted = s.getLong(LAST_COMPACTED_KEY, 0);
|
||||||
|
long elapsed = clock.currentTimeMillis() - lastCompacted;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info(elapsed + " ms since last compaction");
|
||||||
|
return elapsed > MAX_COMPACTION_INTERVAL_MS;
|
||||||
|
}
|
||||||
|
|
||||||
private void storeSchemaVersion(Connection txn, int version)
|
private void storeSchemaVersion(Connection txn, int version)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
@@ -510,6 +518,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void storeLastCompacted(Connection txn) throws DbException {
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putLong(LAST_COMPACTED_KEY, clock.currentTimeMillis());
|
||||||
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isDirty(Settings s) {
|
private boolean isDirty(Settings s) {
|
||||||
return s.getBoolean(DIRTY_KEY, false);
|
return s.getBoolean(DIRTY_KEY, false);
|
||||||
}
|
}
|
||||||
@@ -523,6 +537,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private void initialiseSettings(Connection txn) throws DbException {
|
private void initialiseSettings(Connection txn) throws DbException {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
||||||
|
s.putLong(LAST_COMPACTED_KEY, clock.currentTimeMillis());
|
||||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,8 +592,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
if (closed) throw new DbClosedException();
|
if (closed) throw new DbClosedException();
|
||||||
txn = connectionPool.poll();
|
txn = connections.poll();
|
||||||
logConnectionCounts();
|
|
||||||
} finally {
|
} finally {
|
||||||
connectionsLock.unlock();
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
@@ -589,14 +603,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
txn.setAutoCommit(false);
|
txn.setAutoCommit(false);
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
// The DB may have been closed since the check above
|
|
||||||
if (closed) {
|
|
||||||
tryToClose(txn, LOG, WARNING);
|
|
||||||
throw new DbClosedException();
|
|
||||||
}
|
|
||||||
openConnections++;
|
openConnections++;
|
||||||
logConnectionCounts();
|
|
||||||
connectionsChanged.signalAll();
|
|
||||||
} finally {
|
} finally {
|
||||||
connectionsLock.unlock();
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
@@ -607,91 +614,67 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("connectionsLock")
|
|
||||||
private void logConnectionCounts() {
|
|
||||||
if (LOG.isLoggable(FINE)) {
|
|
||||||
LOG.fine(openConnections + " connections open, "
|
|
||||||
+ connectionPool.size() + " in pool");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void abortTransaction(Connection txn) {
|
public void abortTransaction(Connection txn) {
|
||||||
// The transaction may have been aborted due to an earlier exception,
|
|
||||||
// so close the connection rather than returning it to the pool
|
|
||||||
try {
|
try {
|
||||||
txn.rollback();
|
txn.rollback();
|
||||||
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
|
connections.add(txn);
|
||||||
|
connectionsChanged.signalAll();
|
||||||
|
} finally {
|
||||||
|
connectionsLock.unlock();
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
// Try to close the connection
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
tryToClose(txn, LOG, WARNING);
|
||||||
closeConnection(txn);
|
// Whatever happens, allow the database to close
|
||||||
}
|
connectionsLock.lock();
|
||||||
|
try {
|
||||||
private void closeConnection(Connection txn) {
|
openConnections--;
|
||||||
tryToClose(txn, LOG, WARNING);
|
connectionsChanged.signalAll();
|
||||||
connectionsLock.lock();
|
} finally {
|
||||||
try {
|
connectionsLock.unlock();
|
||||||
openConnections--;
|
}
|
||||||
logConnectionCounts();
|
|
||||||
connectionsChanged.signalAll();
|
|
||||||
} finally {
|
|
||||||
connectionsLock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commitTransaction(Connection txn) throws DbException {
|
public void commitTransaction(Connection txn) throws DbException {
|
||||||
// If the transaction commits successfully then return the connection
|
|
||||||
// to the pool, otherwise close it
|
|
||||||
try {
|
try {
|
||||||
txn.commit();
|
txn.commit();
|
||||||
returnConnectionToPool(txn);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
closeConnection(txn);
|
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void returnConnectionToPool(Connection txn) {
|
|
||||||
boolean shouldClose;
|
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
shouldClose = connectionPool.size() >= MAX_CONNECTION_POOL_SIZE;
|
connections.add(txn);
|
||||||
if (shouldClose) openConnections--;
|
|
||||||
else connectionPool.add(txn);
|
|
||||||
logConnectionCounts();
|
|
||||||
connectionsChanged.signalAll();
|
connectionsChanged.signalAll();
|
||||||
} finally {
|
} finally {
|
||||||
connectionsLock.unlock();
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
if (shouldClose) tryToClose(txn, LOG, WARNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeAllConnections() {
|
void closeAllConnections() throws SQLException {
|
||||||
boolean interrupted = false;
|
boolean interrupted = false;
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
closed = true;
|
closed = true;
|
||||||
for (Connection c : connectionPool) tryToClose(c, LOG, WARNING);
|
for (Connection c : connections) c.close();
|
||||||
openConnections -= connectionPool.size();
|
openConnections -= connections.size();
|
||||||
connectionPool.clear();
|
connections.clear();
|
||||||
while (openConnections > 0) {
|
while (openConnections > 0) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Waiting for " + openConnections
|
|
||||||
+ " connections to be closed");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
connectionsChanged.await();
|
connectionsChanged.await();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.warning("Interrupted while closing connections");
|
LOG.warning("Interrupted while closing connections");
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
}
|
}
|
||||||
for (Connection c : connectionPool) tryToClose(c, LOG, WARNING);
|
for (Connection c : connections) c.close();
|
||||||
openConnections -= connectionPool.size();
|
openConnections -= connections.size();
|
||||||
connectionPool.clear();
|
connections.clear();
|
||||||
}
|
}
|
||||||
LOG.info("All connections closed");
|
|
||||||
} finally {
|
} finally {
|
||||||
connectionsLock.unlock();
|
connectionsLock.unlock();
|
||||||
}
|
}
|
||||||
@@ -934,10 +917,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
String sql = "INSERT INTO statuses (messageId, contactId, groupId,"
|
String sql = "INSERT INTO statuses (messageId, contactId, groupId,"
|
||||||
+ " timestamp, length, state, groupShared, messageShared,"
|
+ " timestamp, length, state, groupShared, messageShared,"
|
||||||
+ " deleted, ack, seen, requested, expiry, txCount,"
|
+ " deleted, ack, seen, requested, expiry, txCount, eta)"
|
||||||
+ " maxLatency)"
|
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, 0, 0,"
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, 0, 0,"
|
||||||
+ " NULL)";
|
+ " 0)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
ps.setInt(2, c.getInt());
|
ps.setInt(2, c.getInt());
|
||||||
@@ -1147,8 +1129,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsAcksToSend(Connection txn, ContactId c)
|
public boolean containsAnythingToSend(Connection txn, ContactId c,
|
||||||
throws DbException {
|
long maxLatency, boolean eager) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -1160,7 +1142,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
boolean acksToSend = rs.next();
|
boolean acksToSend = rs.next();
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return acksToSend;
|
if (acksToSend) return true;
|
||||||
|
if (eager) {
|
||||||
|
sql = "SELECT NULL from statuses"
|
||||||
|
+ " WHERE contactId = ? AND state = ?"
|
||||||
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
|
+ " AND deleted = FALSE AND seen = FALSE";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
|
} else {
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
long eta = now + maxLatency;
|
||||||
|
sql = "SELECT NULL FROM statuses"
|
||||||
|
+ " WHERE contactId = ? AND state = ?"
|
||||||
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
|
+ " AND deleted = FALSE AND seen = FALSE"
|
||||||
|
+ " AND (expiry <= ? OR eta > ?)";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
|
ps.setLong(3, now);
|
||||||
|
ps.setLong(4, eta);
|
||||||
|
}
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean messagesToSend = rs.next();
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
return messagesToSend;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps, LOG, WARNING);
|
||||||
@@ -1280,46 +1289,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsMessagesToSend(Connection txn, ContactId c,
|
|
||||||
long maxLatency, boolean eager) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
if (eager) {
|
|
||||||
String sql = "SELECT NULL from statuses"
|
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
|
||||||
+ " AND deleted = FALSE AND seen = FALSE";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setInt(1, c.getInt());
|
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
|
||||||
} else {
|
|
||||||
long now = clock.currentTimeMillis();
|
|
||||||
String sql = "SELECT NULL FROM statuses"
|
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
|
||||||
+ " AND deleted = FALSE AND seen = FALSE"
|
|
||||||
+ " AND (expiry <= ? OR maxLatency IS NULL"
|
|
||||||
+ " OR ? < maxLatency)";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setInt(1, c.getInt());
|
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
|
||||||
ps.setLong(3, now);
|
|
||||||
ps.setLong(4, maxLatency);
|
|
||||||
}
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
boolean messagesToSend = rs.next();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
return messagesToSend;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(rs, LOG, WARNING);
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsPendingContact(Connection txn, PendingContactId p)
|
public boolean containsPendingContact(Connection txn, PendingContactId p)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -1930,31 +1899,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMessageLength(Connection txn, MessageId m)
|
|
||||||
throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
String sql = "SELECT length from messages"
|
|
||||||
+ " WHERE messageId = ? AND state = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setBytes(1, m.getBytes());
|
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
if (!rs.next()) throw new DbStateException();
|
|
||||||
int length = rs.getInt(1);
|
|
||||||
if (rs.next()) throw new DbStateException();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
return length;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(rs, LOG, WARNING);
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
|
public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
|
||||||
GroupId g) throws DbException {
|
GroupId g) throws DbException {
|
||||||
@@ -2247,6 +2191,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
public Collection<MessageId> getMessagesToOffer(Connection txn,
|
public Collection<MessageId> getMessagesToOffer(Connection txn,
|
||||||
ContactId c, int maxMessages, long maxLatency) throws DbException {
|
ContactId c, int maxMessages, long maxLatency) throws DbException {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
|
long eta = now + maxLatency;
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -2255,14 +2200,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
+ " AND deleted = FALSE"
|
+ " AND deleted = FALSE"
|
||||||
+ " AND seen = FALSE AND requested = FALSE"
|
+ " AND seen = FALSE AND requested = FALSE"
|
||||||
+ " AND (expiry <= ? OR maxLatency IS NULL"
|
+ " AND (expiry <= ? OR eta > ?)"
|
||||||
+ " OR ? < maxLatency)"
|
|
||||||
+ " ORDER BY timestamp LIMIT ?";
|
+ " ORDER BY timestamp LIMIT ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
ps.setLong(4, maxLatency);
|
ps.setLong(4, eta);
|
||||||
ps.setInt(5, maxMessages);
|
ps.setInt(5, maxMessages);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
@@ -2303,9 +2247,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getMessagesToSend(Connection txn,
|
public Collection<MessageId> getMessagesToSend(Connection txn, ContactId c,
|
||||||
ContactId c, long capacity, long maxLatency) throws DbException {
|
int maxLength, long maxLatency) throws DbException {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
|
long eta = now + maxLatency;
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -2314,21 +2259,21 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
+ " AND deleted = FALSE"
|
+ " AND deleted = FALSE"
|
||||||
+ " AND seen = FALSE"
|
+ " AND seen = FALSE"
|
||||||
+ " AND (expiry <= ? OR maxLatency IS NULL"
|
+ " AND (expiry <= ? OR eta > ?)"
|
||||||
+ " OR ? < maxLatency)"
|
|
||||||
+ " ORDER BY timestamp";
|
+ " ORDER BY timestamp";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
ps.setLong(4, maxLatency);
|
ps.setLong(4, eta);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
|
int total = 0;
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
int length = rs.getInt(1);
|
int length = rs.getInt(1);
|
||||||
if (capacity < RECORD_HEADER_BYTES + length) break;
|
if (total + length > maxLength) break;
|
||||||
ids.add(new MessageId(rs.getBytes(2)));
|
ids.add(new MessageId(rs.getBytes(2)));
|
||||||
capacity -= RECORD_HEADER_BYTES + length;
|
total += length;
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -2341,12 +2286,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getUnackedMessagesToSend(Connection txn,
|
public Map<MessageId, Integer> getUnackedMessagesToSend(Connection txn,
|
||||||
ContactId c) throws DbException {
|
ContactId c) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT messageId FROM statuses"
|
String sql = "SELECT length, messageId FROM statuses"
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
+ " WHERE contactId = ? AND state = ?"
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
+ " AND deleted = FALSE AND seen = FALSE"
|
+ " AND deleted = FALSE AND seen = FALSE"
|
||||||
@@ -2355,11 +2300,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<>();
|
Map<MessageId, Integer> results = new LinkedHashMap<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) {
|
||||||
|
int length = rs.getInt(1);
|
||||||
|
MessageId id = new MessageId(rs.getBytes(2));
|
||||||
|
results.put(id, length);
|
||||||
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return results;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs, LOG, WARNING);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps, LOG, WARNING);
|
||||||
@@ -2472,7 +2421,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
MessageId m = new MessageId(rs.getBytes(1));
|
MessageId m = new MessageId(rs.getBytes(1));
|
||||||
GroupId g = new GroupId(rs.getBytes(2));
|
GroupId g = new GroupId(rs.getBytes(2));
|
||||||
Collection<MessageId> messageIds = ids.get(g);
|
Collection<MessageId> messageIds = ids.get(g);
|
||||||
//noinspection Java8MapApi
|
|
||||||
if (messageIds == null) {
|
if (messageIds == null) {
|
||||||
messageIds = new ArrayList<>();
|
messageIds = new ArrayList<>();
|
||||||
ids.put(g, messageIds);
|
ids.put(g, messageIds);
|
||||||
@@ -2490,28 +2438,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNextSendTime(Connection txn, ContactId c, long maxLatency)
|
public long getNextSendTime(Connection txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
// Are any messages sendable immediately?
|
String sql = "SELECT expiry FROM statuses"
|
||||||
String sql = "SELECT NULL FROM statuses"
|
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
|
||||||
+ " AND deleted = FALSE AND seen = FALSE"
|
|
||||||
+ " AND (maxLatency IS NULL OR ? < maxLatency)";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setInt(1, c.getInt());
|
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
|
||||||
ps.setLong(3, maxLatency);
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
boolean found = rs.next();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
if (found) return 0;
|
|
||||||
// When is the earliest expiry time (could be in the past)?
|
|
||||||
sql = "SELECT expiry FROM statuses"
|
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
+ " WHERE contactId = ? AND state = ?"
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
+ " AND deleted = FALSE AND seen = FALSE"
|
+ " AND deleted = FALSE AND seen = FALSE"
|
||||||
@@ -2615,8 +2547,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getRequestedMessagesToSend(Connection txn,
|
public Collection<MessageId> getRequestedMessagesToSend(Connection txn,
|
||||||
ContactId c, long capacity, long maxLatency) throws DbException {
|
ContactId c, int maxLength, long maxLatency) throws DbException {
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
|
long eta = now + maxLatency;
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -2625,21 +2558,21 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
||||||
+ " AND deleted = FALSE"
|
+ " AND deleted = FALSE"
|
||||||
+ " AND seen = FALSE AND requested = TRUE"
|
+ " AND seen = FALSE AND requested = TRUE"
|
||||||
+ " AND (expiry <= ? OR maxLatency IS NULL"
|
+ " AND (expiry <= ? OR eta > ?)"
|
||||||
+ " OR ? < maxLatency)"
|
|
||||||
+ " ORDER BY timestamp";
|
+ " ORDER BY timestamp";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
ps.setLong(4, maxLatency);
|
ps.setLong(4, eta);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
|
int total = 0;
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
int length = rs.getInt(1);
|
int length = rs.getInt(1);
|
||||||
if (capacity < RECORD_HEADER_BYTES + length) break;
|
if (total + length > maxLength) break;
|
||||||
ids.add(new MessageId(rs.getBytes(2)));
|
ids.add(new MessageId(rs.getBytes(2)));
|
||||||
capacity -= RECORD_HEADER_BYTES + length;
|
total += length;
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -2793,7 +2726,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ContactId c = new ContactId(rs.getInt(1));
|
ContactId c = new ContactId(rs.getInt(1));
|
||||||
TransportId t = new TransportId(rs.getString(2));
|
TransportId t = new TransportId(rs.getString(2));
|
||||||
Collection<TransportId> transportIds = ids.get(c);
|
Collection<TransportId> transportIds = ids.get(c);
|
||||||
//noinspection Java8MapApi
|
|
||||||
if (transportIds == null) {
|
if (transportIds == null) {
|
||||||
transportIds = new ArrayList<>();
|
transportIds = new ArrayList<>();
|
||||||
ids.put(c, transportIds);
|
ids.put(c, transportIds);
|
||||||
@@ -3358,30 +3290,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetUnackedMessagesToSend(Connection txn, ContactId c)
|
|
||||||
throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
String sql = "UPDATE statuses SET expiry = 0, txCount = 0,"
|
|
||||||
+ " maxLatency = NULL"
|
|
||||||
+ " WHERE contactId = ? AND state = ?"
|
|
||||||
+ " AND groupShared = TRUE AND messageShared = TRUE"
|
|
||||||
+ " AND deleted = FALSE AND seen = FALSE";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setInt(1, c.getInt());
|
|
||||||
ps.setInt(2, DELIVERED.getValue());
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if (affected < 0) {
|
|
||||||
throw new DbStateException();
|
|
||||||
}
|
|
||||||
ps.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(ps, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCleanupTimerDuration(Connection txn, MessageId m,
|
public void setCleanupTimerDuration(Connection txn, MessageId m,
|
||||||
long duration) throws DbException {
|
long duration) throws DbException {
|
||||||
@@ -3709,8 +3617,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateRetransmissionData(Connection txn, ContactId c,
|
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
||||||
MessageId m, long maxLatency) throws DbException {
|
long maxLatency) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
@@ -3726,12 +3634,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
sql = "UPDATE statuses"
|
sql = "UPDATE statuses"
|
||||||
+ " SET expiry = ?, txCount = txCount + 1, maxLatency = ?"
|
+ " SET expiry = ?, txCount = txCount + 1, eta = ?"
|
||||||
+ " WHERE messageId = ? AND contactId = ?";
|
+ " WHERE messageId = ? AND contactId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
long now = clock.currentTimeMillis();
|
long now = clock.currentTimeMillis();
|
||||||
|
long eta = now + maxLatency;
|
||||||
ps.setLong(1, calculateExpiry(now, maxLatency, txCount));
|
ps.setLong(1, calculateExpiry(now, maxLatency, txCount));
|
||||||
ps.setLong(2, maxLatency);
|
ps.setLong(2, eta);
|
||||||
ps.setBytes(3, m.getBytes());
|
ps.setBytes(3, m.getBytes());
|
||||||
ps.setInt(4, c.getInt());
|
ps.setInt(4, c.getInt());
|
||||||
int affected = ps.executeUpdate();
|
int affected = ps.executeUpdate();
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
package org.briarproject.bramble.db;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
|
||||||
|
|
||||||
class Migration49_50 implements Migration<Connection> {
|
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(Migration49_50.class.getName());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStartVersion() {
|
|
||||||
return 49;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getEndVersion() {
|
|
||||||
return 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void migrate(Connection txn) throws DbException {
|
|
||||||
Statement s = null;
|
|
||||||
try {
|
|
||||||
s = txn.createStatement();
|
|
||||||
s.execute("ALTER TABLE statuses"
|
|
||||||
+ " ALTER COLUMN eta"
|
|
||||||
+ " RENAME TO maxLatency");
|
|
||||||
s.execute("ALTER TABLE statuses"
|
|
||||||
+ " ALTER COLUMN maxLatency"
|
|
||||||
+ " SET NULL");
|
|
||||||
s.execute("UPDATE statuses SET maxLatency = NULL");
|
|
||||||
} catch (SQLException e) {
|
|
||||||
tryToClose(s, LOG, WARNING);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +1,18 @@
|
|||||||
package org.briarproject.bramble.io;
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import okhttp3.Dns;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class IoModule {
|
public class IoModule {
|
||||||
|
|
||||||
private static final int CONNECT_TIMEOUT = 60_000; // Milliseconds
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
|
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
|
||||||
return timeoutMonitor;
|
return timeoutMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share an HTTP client instance between requests where possible, while
|
|
||||||
// allowing the client to be garbage-collected between requests. The
|
|
||||||
// provider keeps a weak reference to the last client instance and reuses
|
|
||||||
// the instance until it gets garbage-collected. See
|
|
||||||
// https://medium.com/@leandromazzuquini/if-you-are-using-okhttp-you-should-know-this-61d68e065a2b
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
WeakSingletonProvider<OkHttpClient> provideOkHttpClientProvider(
|
|
||||||
SocketFactory torSocketFactory, Dns noDnsLookups) {
|
|
||||||
return new WeakSingletonProvider<OkHttpClient>() {
|
|
||||||
@Override
|
|
||||||
@Nonnull
|
|
||||||
public OkHttpClient createInstance() {
|
|
||||||
return new OkHttpClient.Builder()
|
|
||||||
.socketFactory(torSocketFactory)
|
|
||||||
.dns(noDnsLookups) // Don't make local DNS lookups
|
|
||||||
.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.briarproject.bramble.io;
|
package org.briarproject.bramble.io;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
|
||||||
import org.briarproject.bramble.api.system.Wakeful;
|
import org.briarproject.bramble.api.system.Wakeful;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -190,10 +190,6 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (state == STOPPING) {
|
|
||||||
LOG.info("Already stopped");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG.info("Stopping services");
|
LOG.info("Stopping services");
|
||||||
state = STOPPING;
|
state = STOPPING;
|
||||||
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for calling an API endpoint with the option to retry the call.
|
|
||||||
*/
|
|
||||||
interface ApiCall {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method makes a synchronous call to an API endpoint and returns
|
|
||||||
* true if the call should be retried, in which case the method may be
|
|
||||||
* called again on the same {@link ApiCall} instance after a delay.
|
|
||||||
*
|
|
||||||
* @return True if the API call needs to be retried, or false if the API
|
|
||||||
* call succeeded or {@link TolerableFailureException failed tolerably}.
|
|
||||||
*/
|
|
||||||
@IoExecutor
|
|
||||||
boolean callApi();
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for checking whether a mailbox is reachable.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
interface ConnectivityChecker {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the checker. Any current connectivity check is cancelled.
|
|
||||||
*/
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a connectivity check if needed and calls the given observer when
|
|
||||||
* the check succeeds. If a check is already running then the observer is
|
|
||||||
* called when the check succeeds. If a connectivity check has recently
|
|
||||||
* succeeded then the observer is called immediately.
|
|
||||||
* <p>
|
|
||||||
* Observers are removed after being called, or when the checker is
|
|
||||||
* {@link #destroy() destroyed}.
|
|
||||||
*/
|
|
||||||
void checkConnectivity(MailboxProperties properties,
|
|
||||||
ConnectivityObserver o);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an observer that was added via
|
|
||||||
* {@link #checkConnectivity(MailboxProperties, ConnectivityObserver)}. If
|
|
||||||
* there are no remaining observers and a connectivity check is running
|
|
||||||
* then the check will be cancelled.
|
|
||||||
*/
|
|
||||||
void removeObserver(ConnectivityObserver o);
|
|
||||||
|
|
||||||
interface ConnectivityObserver {
|
|
||||||
void onConnectivityCheckSucceeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class ConnectivityCheckerImpl implements ConnectivityChecker {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If no more than this much time has elapsed since the last connectivity
|
|
||||||
* check succeeded, consider the result to be fresh and don't check again.
|
|
||||||
* <p>
|
|
||||||
* Package access for testing.
|
|
||||||
*/
|
|
||||||
static final long CONNECTIVITY_CHECK_FRESHNESS_MS = 10_000;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
protected final Clock clock;
|
|
||||||
private final MailboxApiCaller mailboxApiCaller;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean destroyed = false;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Cancellable connectivityCheck = null;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private long lastConnectivityCheckSucceeded = 0;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private final List<ConnectivityObserver> connectivityObservers =
|
|
||||||
new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an {@link ApiCall} for checking whether the mailbox is
|
|
||||||
* reachable. The {@link ApiCall} should call
|
|
||||||
* {@link #onConnectivityCheckSucceeded(long)} if the check succeeds.
|
|
||||||
*/
|
|
||||||
abstract ApiCall createConnectivityCheckTask(MailboxProperties properties);
|
|
||||||
|
|
||||||
ConnectivityCheckerImpl(Clock clock, MailboxApiCaller mailboxApiCaller) {
|
|
||||||
this.clock = clock;
|
|
||||||
this.mailboxApiCaller = mailboxApiCaller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
synchronized (lock) {
|
|
||||||
destroyed = true;
|
|
||||||
connectivityObservers.clear();
|
|
||||||
if (connectivityCheck != null) {
|
|
||||||
connectivityCheck.cancel();
|
|
||||||
connectivityCheck = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkConnectivity(MailboxProperties properties,
|
|
||||||
ConnectivityObserver o) {
|
|
||||||
boolean callNow = false;
|
|
||||||
synchronized (lock) {
|
|
||||||
if (destroyed) return;
|
|
||||||
if (connectivityCheck == null) {
|
|
||||||
// No connectivity check is running
|
|
||||||
long now = clock.currentTimeMillis();
|
|
||||||
if (now - lastConnectivityCheckSucceeded
|
|
||||||
> CONNECTIVITY_CHECK_FRESHNESS_MS) {
|
|
||||||
// The last connectivity check is stale, start a new one
|
|
||||||
connectivityObservers.add(o);
|
|
||||||
ApiCall task = createConnectivityCheckTask(properties);
|
|
||||||
connectivityCheck = mailboxApiCaller.retryWithBackoff(task);
|
|
||||||
} else {
|
|
||||||
// The last connectivity check is fresh
|
|
||||||
callNow = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// A connectivity check is running, wait for it to succeed
|
|
||||||
connectivityObservers.add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (callNow) o.onConnectivityCheckSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onConnectivityCheckSucceeded(long now) {
|
|
||||||
List<ConnectivityObserver> observers;
|
|
||||||
synchronized (lock) {
|
|
||||||
if (destroyed) return;
|
|
||||||
connectivityCheck = null;
|
|
||||||
lastConnectivityCheckSucceeded = now;
|
|
||||||
observers = new ArrayList<>(connectivityObservers);
|
|
||||||
connectivityObservers.clear();
|
|
||||||
}
|
|
||||||
for (ConnectivityObserver o : observers) {
|
|
||||||
o.onConnectivityCheckSucceeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeObserver(ConnectivityObserver o) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (destroyed) return;
|
|
||||||
connectivityObservers.remove(o);
|
|
||||||
if (connectivityObservers.isEmpty() && connectivityCheck != null) {
|
|
||||||
connectivityCheck.cancel();
|
|
||||||
connectivityCheck = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class ContactMailboxClient implements MailboxClient {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(ContactMailboxClient.class.getName());
|
|
||||||
|
|
||||||
private final MailboxWorkerFactory workerFactory;
|
|
||||||
private final ConnectivityChecker connectivityChecker;
|
|
||||||
private final TorReachabilityMonitor reachabilityMonitor;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private MailboxWorker uploadWorker = null, downloadWorker = null;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ContactMailboxClient(MailboxWorkerFactory workerFactory,
|
|
||||||
ConnectivityChecker connectivityChecker,
|
|
||||||
TorReachabilityMonitor reachabilityMonitor) {
|
|
||||||
this.workerFactory = workerFactory;
|
|
||||||
this.connectivityChecker = connectivityChecker;
|
|
||||||
this.reachabilityMonitor = reachabilityMonitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
LOG.info("Started");
|
|
||||||
// Nothing to do until contact is assigned
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
LOG.info("Destroyed");
|
|
||||||
MailboxWorker uploadWorker, downloadWorker;
|
|
||||||
synchronized (lock) {
|
|
||||||
uploadWorker = this.uploadWorker;
|
|
||||||
this.uploadWorker = null;
|
|
||||||
downloadWorker = this.downloadWorker;
|
|
||||||
this.downloadWorker = null;
|
|
||||||
}
|
|
||||||
if (uploadWorker != null) uploadWorker.destroy();
|
|
||||||
if (downloadWorker != null) downloadWorker.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void assignContactForUpload(ContactId contactId,
|
|
||||||
MailboxProperties properties, MailboxFolderId folderId) {
|
|
||||||
LOG.info("Contact assigned for upload");
|
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
|
||||||
// For a contact's mailbox we should always be uploading to the outbox
|
|
||||||
// assigned to us by the contact
|
|
||||||
if (!folderId.equals(properties.getOutboxId())) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
MailboxWorker uploadWorker = workerFactory.createUploadWorker(
|
|
||||||
connectivityChecker, properties, folderId, contactId);
|
|
||||||
synchronized (lock) {
|
|
||||||
if (this.uploadWorker != null) throw new IllegalStateException();
|
|
||||||
this.uploadWorker = uploadWorker;
|
|
||||||
}
|
|
||||||
uploadWorker.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deassignContactForUpload(ContactId contactId) {
|
|
||||||
LOG.info("Contact deassigned for upload");
|
|
||||||
MailboxWorker uploadWorker;
|
|
||||||
synchronized (lock) {
|
|
||||||
uploadWorker = this.uploadWorker;
|
|
||||||
this.uploadWorker = null;
|
|
||||||
}
|
|
||||||
if (uploadWorker != null) uploadWorker.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void assignContactForDownload(ContactId contactId,
|
|
||||||
MailboxProperties properties, MailboxFolderId folderId) {
|
|
||||||
LOG.info("Contact assigned for download");
|
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
|
||||||
// For a contact's mailbox we should always be downloading from the
|
|
||||||
// inbox assigned to us by the contact
|
|
||||||
if (!folderId.equals(properties.getInboxId())) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
MailboxWorker downloadWorker =
|
|
||||||
workerFactory.createDownloadWorkerForContactMailbox(
|
|
||||||
connectivityChecker, reachabilityMonitor, properties);
|
|
||||||
synchronized (lock) {
|
|
||||||
if (this.downloadWorker != null) throw new IllegalStateException();
|
|
||||||
this.downloadWorker = downloadWorker;
|
|
||||||
}
|
|
||||||
downloadWorker.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deassignContactForDownload(ContactId contactId) {
|
|
||||||
LOG.info("Contact deassigned for download");
|
|
||||||
MailboxWorker downloadWorker;
|
|
||||||
synchronized (lock) {
|
|
||||||
downloadWorker = this.downloadWorker;
|
|
||||||
this.downloadWorker = null;
|
|
||||||
}
|
|
||||||
if (downloadWorker != null) downloadWorker.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
|
||||||
|
|
||||||
private final MailboxApi mailboxApi;
|
|
||||||
|
|
||||||
ContactMailboxConnectivityChecker(Clock clock,
|
|
||||||
MailboxApiCaller mailboxApiCaller, MailboxApi mailboxApi) {
|
|
||||||
super(clock, mailboxApiCaller);
|
|
||||||
this.mailboxApi = mailboxApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
|
||||||
return new SimpleApiCall(() -> {
|
|
||||||
if (!mailboxApi.checkStatus(properties)) throw new ApiException();
|
|
||||||
// Call the observers and cache the result
|
|
||||||
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
|
||||||
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class ContactMailboxDownloadWorker implements MailboxWorker,
|
|
||||||
ConnectivityObserver, TorReachabilityObserver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the worker is started it waits for a connectivity check, then
|
|
||||||
* starts its first download cycle: checking the inbox, downloading and
|
|
||||||
* deleting any files, and checking again until the inbox is empty.
|
|
||||||
* <p>
|
|
||||||
* The worker then waits for our Tor hidden service to be reachable before
|
|
||||||
* starting its second download cycle. This ensures that if a contact
|
|
||||||
* tried and failed to connect to our hidden service before it was
|
|
||||||
* reachable, and therefore uploaded a file to the mailbox instead, we'll
|
|
||||||
* find the file in the second download cycle.
|
|
||||||
*/
|
|
||||||
private enum State {
|
|
||||||
CREATED,
|
|
||||||
CONNECTIVITY_CHECK,
|
|
||||||
DOWNLOAD_CYCLE_1,
|
|
||||||
WAITING_FOR_TOR,
|
|
||||||
DOWNLOAD_CYCLE_2,
|
|
||||||
FINISHED,
|
|
||||||
DESTROYED
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(ContactMailboxDownloadWorker.class.getName());
|
|
||||||
|
|
||||||
private final ConnectivityChecker connectivityChecker;
|
|
||||||
private final TorReachabilityMonitor torReachabilityMonitor;
|
|
||||||
private final MailboxApiCaller mailboxApiCaller;
|
|
||||||
private final MailboxApi mailboxApi;
|
|
||||||
private final MailboxFileManager mailboxFileManager;
|
|
||||||
private final MailboxProperties mailboxProperties;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private State state = State.CREATED;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Cancellable apiCall = null;
|
|
||||||
|
|
||||||
ContactMailboxDownloadWorker(
|
|
||||||
ConnectivityChecker connectivityChecker,
|
|
||||||
TorReachabilityMonitor torReachabilityMonitor,
|
|
||||||
MailboxApiCaller mailboxApiCaller,
|
|
||||||
MailboxApi mailboxApi,
|
|
||||||
MailboxFileManager mailboxFileManager,
|
|
||||||
MailboxProperties mailboxProperties) {
|
|
||||||
if (mailboxProperties.isOwner()) throw new IllegalArgumentException();
|
|
||||||
this.connectivityChecker = connectivityChecker;
|
|
||||||
this.torReachabilityMonitor = torReachabilityMonitor;
|
|
||||||
this.mailboxApiCaller = mailboxApiCaller;
|
|
||||||
this.mailboxApi = mailboxApi;
|
|
||||||
this.mailboxFileManager = mailboxFileManager;
|
|
||||||
this.mailboxProperties = mailboxProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
LOG.info("Started");
|
|
||||||
synchronized (lock) {
|
|
||||||
// Don't allow the worker to be reused
|
|
||||||
if (state != State.CREATED) return;
|
|
||||||
state = State.CONNECTIVITY_CHECK;
|
|
||||||
}
|
|
||||||
// Avoid leaking observer in case destroy() is called concurrently
|
|
||||||
// before observer is added
|
|
||||||
connectivityChecker.checkConnectivity(mailboxProperties, this);
|
|
||||||
boolean destroyed;
|
|
||||||
synchronized (lock) {
|
|
||||||
destroyed = state == State.DESTROYED;
|
|
||||||
}
|
|
||||||
if (destroyed) connectivityChecker.removeObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
LOG.info("Destroyed");
|
|
||||||
Cancellable apiCall;
|
|
||||||
synchronized (lock) {
|
|
||||||
state = State.DESTROYED;
|
|
||||||
apiCall = this.apiCall;
|
|
||||||
this.apiCall = null;
|
|
||||||
}
|
|
||||||
if (apiCall != null) apiCall.cancel();
|
|
||||||
connectivityChecker.removeObserver(this);
|
|
||||||
torReachabilityMonitor.removeObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectivityCheckSucceeded() {
|
|
||||||
LOG.info("Connectivity check succeeded");
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state != State.CONNECTIVITY_CHECK) return;
|
|
||||||
state = State.DOWNLOAD_CYCLE_1;
|
|
||||||
// Start first download cycle
|
|
||||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
|
||||||
new SimpleApiCall(this::apiCallListInbox));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void apiCallListInbox() throws IOException, ApiException {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
}
|
|
||||||
LOG.info("Listing inbox");
|
|
||||||
List<MailboxFile> files = mailboxApi.getFiles(mailboxProperties,
|
|
||||||
requireNonNull(mailboxProperties.getInboxId()));
|
|
||||||
if (files.isEmpty()) onDownloadCycleFinished();
|
|
||||||
else downloadNextFile(new LinkedList<>(files));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onDownloadCycleFinished() {
|
|
||||||
boolean addObserver = false;
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DOWNLOAD_CYCLE_1) {
|
|
||||||
LOG.info("First download cycle finished");
|
|
||||||
state = State.WAITING_FOR_TOR;
|
|
||||||
apiCall = null;
|
|
||||||
addObserver = true;
|
|
||||||
} else if (state == State.DOWNLOAD_CYCLE_2) {
|
|
||||||
LOG.info("Second download cycle finished");
|
|
||||||
state = State.FINISHED;
|
|
||||||
apiCall = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (addObserver) {
|
|
||||||
// Avoid leaking observer in case destroy() is called concurrently
|
|
||||||
// before observer is added
|
|
||||||
torReachabilityMonitor.addOneShotObserver(this);
|
|
||||||
boolean destroyed;
|
|
||||||
synchronized (lock) {
|
|
||||||
destroyed = state == State.DESTROYED;
|
|
||||||
}
|
|
||||||
if (destroyed) torReachabilityMonitor.removeObserver(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void downloadNextFile(Queue<MailboxFile> queue) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
MailboxFile file = queue.remove();
|
|
||||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
|
||||||
new SimpleApiCall(() -> apiCallDownloadFile(file, queue)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void apiCallDownloadFile(MailboxFile file,
|
|
||||||
Queue<MailboxFile> queue) throws IOException, ApiException {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
}
|
|
||||||
LOG.info("Downloading file");
|
|
||||||
File tempFile = mailboxFileManager.createTempFileForDownload();
|
|
||||||
try {
|
|
||||||
mailboxApi.getFile(mailboxProperties,
|
|
||||||
requireNonNull(mailboxProperties.getInboxId()),
|
|
||||||
file.name, tempFile);
|
|
||||||
} catch (IOException | ApiException e) {
|
|
||||||
if (!tempFile.delete()) {
|
|
||||||
LOG.warning("Failed to delete temporary file");
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
mailboxFileManager.handleDownloadedFile(tempFile);
|
|
||||||
deleteFile(file, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFile(MailboxFile file, Queue<MailboxFile> queue) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
|
||||||
new SimpleApiCall(() -> apiCallDeleteFile(file, queue)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void apiCallDeleteFile(MailboxFile file, Queue<MailboxFile> queue)
|
|
||||||
throws IOException, ApiException {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mailboxApi.deleteFile(mailboxProperties,
|
|
||||||
requireNonNull(mailboxProperties.getInboxId()), file.name);
|
|
||||||
} catch (TolerableFailureException e) {
|
|
||||||
// Catch this so we can continue to the next file
|
|
||||||
logException(LOG, INFO, e);
|
|
||||||
}
|
|
||||||
if (queue.isEmpty()) {
|
|
||||||
// List the inbox again to check for files that may have arrived
|
|
||||||
// while we were downloading
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state == State.DESTROYED) return;
|
|
||||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
|
||||||
new SimpleApiCall(this::apiCallListInbox));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
downloadNextFile(queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTorReachable() {
|
|
||||||
LOG.info("Our Tor hidden service is reachable");
|
|
||||||
synchronized (lock) {
|
|
||||||
if (state != State.WAITING_FOR_TOR) return;
|
|
||||||
state = State.DOWNLOAD_CYCLE_2;
|
|
||||||
// Start second download cycle
|
|
||||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
|
||||||
new SimpleApiCall(this::apiCallListInbox));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
interface MailboxApi {
|
|
||||||
|
|
||||||
List<MailboxVersion> getServerSupports(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the mailbox with the setup token.
|
|
||||||
*
|
|
||||||
* @param properties MailboxProperties with the setup token
|
|
||||||
* @return the owner token
|
|
||||||
* @throws ApiException for 401 response.
|
|
||||||
*/
|
|
||||||
MailboxProperties setup(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the status of the mailbox.
|
|
||||||
*
|
|
||||||
* @return true if the status is OK, false otherwise.
|
|
||||||
* @throws ApiException for 401 response.
|
|
||||||
*/
|
|
||||||
boolean checkStatus(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpairs Briar and the mailbox (owner only).
|
|
||||||
* Resets mailbox state to that after first install
|
|
||||||
* (e.g. removes all stored files as well).
|
|
||||||
*/
|
|
||||||
void wipeMailbox(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new contact to the mailbox.
|
|
||||||
*
|
|
||||||
* @throws TolerableFailureException if response code is 409
|
|
||||||
* (contact was already added).
|
|
||||||
*/
|
|
||||||
void addContact(MailboxProperties properties, MailboxContact contact)
|
|
||||||
throws IOException, ApiException, TolerableFailureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a contact from the mailbox.
|
|
||||||
* This should get called after a contact was removed from Briar.
|
|
||||||
*
|
|
||||||
* @throws TolerableFailureException if response code is 404
|
|
||||||
* (contact probably was already deleted).
|
|
||||||
*/
|
|
||||||
void deleteContact(MailboxProperties properties, ContactId contactId)
|
|
||||||
throws IOException, ApiException, TolerableFailureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of {@link ContactId}s from the mailbox.
|
|
||||||
* These are the contacts that the mailbox already knows about.
|
|
||||||
*/
|
|
||||||
Collection<ContactId> getContacts(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by contacts to send files to the owner
|
|
||||||
* and by the owner to send files to contacts.
|
|
||||||
* <p>
|
|
||||||
* The owner can add files to the contacts' inboxes
|
|
||||||
* and the contacts can add files to their own outbox.
|
|
||||||
*/
|
|
||||||
void addFile(MailboxProperties properties, MailboxFolderId folderId,
|
|
||||||
File file) throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by owner and contacts to list their files to retrieve.
|
|
||||||
* <p>
|
|
||||||
* Returns 200 OK with the list of files in JSON.
|
|
||||||
*/
|
|
||||||
List<MailboxFile> getFiles(MailboxProperties properties,
|
|
||||||
MailboxFolderId folderId) throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by owner and contacts to retrieve a file.
|
|
||||||
* <p>
|
|
||||||
* Returns 200 OK if successful with the files' raw bytes
|
|
||||||
* in the response body.
|
|
||||||
*
|
|
||||||
* @param file the empty file the response bytes will be written into.
|
|
||||||
*/
|
|
||||||
void getFile(MailboxProperties properties, MailboxFolderId folderId,
|
|
||||||
MailboxFileId fileId, File file) throws IOException, ApiException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by owner and contacts to delete files.
|
|
||||||
* <p>
|
|
||||||
* Returns 200 OK (no exception) if deletion was successful.
|
|
||||||
*
|
|
||||||
* @throws TolerableFailureException on 404 response,
|
|
||||||
* because file was most likely deleted already.
|
|
||||||
*/
|
|
||||||
void deleteFile(MailboxProperties properties, MailboxFolderId folderId,
|
|
||||||
MailboxFileId fileId)
|
|
||||||
throws IOException, ApiException, TolerableFailureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists all contact outboxes that have files available
|
|
||||||
* for the owner to download.
|
|
||||||
*
|
|
||||||
* @return a list of folder names
|
|
||||||
* to be used with {@link #getFiles(MailboxProperties, MailboxFolderId)}.
|
|
||||||
* @throws IllegalArgumentException if used by non-owner.
|
|
||||||
*/
|
|
||||||
List<MailboxFolderId> getFolders(MailboxProperties properties)
|
|
||||||
throws IOException, ApiException;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@JsonSerialize
|
|
||||||
class MailboxContact {
|
|
||||||
public final int contactId;
|
|
||||||
public final MailboxAuthToken token;
|
|
||||||
public final MailboxFolderId inboxId, outboxId;
|
|
||||||
|
|
||||||
MailboxContact(ContactId contactId,
|
|
||||||
MailboxAuthToken token,
|
|
||||||
MailboxFolderId inboxId,
|
|
||||||
MailboxFolderId outboxId) {
|
|
||||||
this.contactId = contactId.getInt();
|
|
||||||
this.token = token;
|
|
||||||
this.inboxId = inboxId;
|
|
||||||
this.outboxId = outboxId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerialize
|
|
||||||
class MailboxFile implements Comparable<MailboxFile> {
|
|
||||||
public final MailboxFileId name;
|
|
||||||
public final long time;
|
|
||||||
|
|
||||||
public MailboxFile(MailboxFileId name, long time) {
|
|
||||||
this.name = name;
|
|
||||||
this.time = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(@Nonnull MailboxApi.MailboxFile mailboxFile) {
|
|
||||||
//noinspection UseCompareMethod
|
|
||||||
return time < mailboxFile.time ? -1 :
|
|
||||||
(time == mailboxFile.time ? 0 : 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class ApiException extends Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class MailboxAlreadyPairedException extends ApiException {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A failure that does not need to be retried,
|
|
||||||
* e.g. when adding a contact that already exists.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
class TolerableFailureException extends Exception {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.DAYS;
|
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
interface MailboxApiCaller {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The minimum interval between retries in milliseconds.
|
|
||||||
*/
|
|
||||||
long MIN_RETRY_INTERVAL_MS = MINUTES.toMillis(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum interval between retries in milliseconds.
|
|
||||||
*/
|
|
||||||
long MAX_RETRY_INTERVAL_MS = DAYS.toMillis(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously calls the given API call on the {@link IoExecutor},
|
|
||||||
* automatically retrying at increasing intervals until the API call
|
|
||||||
* returns false or retries are cancelled.
|
|
||||||
* <p>
|
|
||||||
* This method is safe to call while holding a lock.
|
|
||||||
*
|
|
||||||
* @return A {@link Cancellable} that can be used to cancel any future
|
|
||||||
* retries.
|
|
||||||
*/
|
|
||||||
Cancellable retryWithBackoff(ApiCall apiCall);
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Cancellable;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.lang.Math.min;
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class MailboxApiCallerImpl implements MailboxApiCaller {
|
|
||||||
|
|
||||||
private final TaskScheduler taskScheduler;
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MailboxApiCallerImpl(TaskScheduler taskScheduler,
|
|
||||||
@IoExecutor Executor ioExecutor) {
|
|
||||||
this.taskScheduler = taskScheduler;
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cancellable retryWithBackoff(ApiCall apiCall) {
|
|
||||||
Task task = new Task(apiCall);
|
|
||||||
task.start();
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Task implements Cancellable {
|
|
||||||
|
|
||||||
private final ApiCall apiCall;
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Cancellable scheduledTask = null;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean cancelled = false;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private long retryIntervalMs = MIN_RETRY_INTERVAL_MS;
|
|
||||||
|
|
||||||
private Task(ApiCall apiCall) {
|
|
||||||
this.apiCall = apiCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void start() {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (cancelled) throw new AssertionError();
|
|
||||||
ioExecutor.execute(this::callApi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IoExecutor
|
|
||||||
private void callApi() {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (cancelled) return;
|
|
||||||
}
|
|
||||||
// The call returns true if we should retry
|
|
||||||
if (apiCall.callApi()) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (cancelled) return;
|
|
||||||
scheduledTask = taskScheduler.schedule(this::callApi,
|
|
||||||
ioExecutor, retryIntervalMs, MILLISECONDS);
|
|
||||||
// Increase the retry interval each time we retry
|
|
||||||
retryIntervalMs =
|
|
||||||
min(MAX_RETRY_INTERVAL_MS, retryIntervalMs * 2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
synchronized (lock) {
|
|
||||||
scheduledTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
Cancellable scheduledTask;
|
|
||||||
synchronized (lock) {
|
|
||||||
cancelled = true;
|
|
||||||
scheduledTask = this.scheduledTask;
|
|
||||||
this.scheduledTask = null;
|
|
||||||
}
|
|
||||||
if (scheduledTask != null) scheduledTask.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user