mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Compare commits
89 Commits
alpha-1.4.
...
alpha-1.4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dd4d86f4a | ||
|
|
17e0829f42 | ||
|
|
938d8b71a0 | ||
|
|
1b808584b6 | ||
|
|
36db5b48ef | ||
|
|
ccd6ed9ff0 | ||
|
|
0ced10b3a9 | ||
|
|
98064e9efe | ||
|
|
63172ef2e4 | ||
|
|
7a854e70cb | ||
|
|
ac8a4db457 | ||
|
|
5a09530670 | ||
|
|
4fe91bacc6 | ||
|
|
7f70a1519b | ||
|
|
c92ee0458e | ||
|
|
10b1fe756d | ||
|
|
1a2a250be0 | ||
|
|
a621b8077e | ||
|
|
19084d4060 | ||
|
|
2f73ee1b57 | ||
|
|
45fa12c0b3 | ||
|
|
4253bbaaf5 | ||
|
|
8c2e58796b | ||
|
|
3f13e7e9c3 | ||
|
|
421a93b9a6 | ||
|
|
8a088638db | ||
|
|
a888c5f632 | ||
|
|
0b94814620 | ||
|
|
e82e11acfa | ||
|
|
795461d9a8 | ||
|
|
7b8d01cfe0 | ||
|
|
abd04ee7f5 | ||
|
|
cc5365eaf0 | ||
|
|
6b20b03698 | ||
|
|
9da7fbf4f6 | ||
|
|
f64f442fcf | ||
|
|
6eda2f6d13 | ||
|
|
6faa095dfb | ||
|
|
4007fca668 | ||
|
|
28a747f7f3 | ||
|
|
fd2d5c9173 | ||
|
|
8f7bb9d26b | ||
|
|
dc220200b6 | ||
|
|
0cea137d75 | ||
|
|
2eef34f424 | ||
|
|
a68fff9dd2 | ||
|
|
ddc8f4a7d7 | ||
|
|
f961b6a80b | ||
|
|
93439d9c17 | ||
|
|
f3ee884816 | ||
|
|
8ca22043cf | ||
|
|
9353b78da8 | ||
|
|
429bbe1275 | ||
|
|
c5fb1416bd | ||
|
|
e52cbd896e | ||
|
|
ab1b8784b7 | ||
|
|
55a4daa92f | ||
|
|
e52250f1e4 | ||
|
|
33d01aac8c | ||
|
|
b920382e44 | ||
|
|
1a2f85f701 | ||
|
|
186bcc0b47 | ||
|
|
8b9140f477 | ||
|
|
f959c32935 | ||
|
|
1c060bc6db | ||
|
|
5e44e4d308 | ||
|
|
75d5dec45f | ||
|
|
d825227eb5 | ||
|
|
967dd1f18d | ||
|
|
4a4147b563 | ||
|
|
08b72af647 | ||
|
|
528e090c6f | ||
|
|
652f9e5705 | ||
|
|
6a91ec7a6b | ||
|
|
c3a9eff96b | ||
|
|
bd05d893eb | ||
|
|
6965bc0acd | ||
|
|
c6e9554026 | ||
|
|
ab8734e373 | ||
|
|
267956b36c | ||
|
|
ec84ddb38b | ||
|
|
ba2db48d8e | ||
|
|
186f61f771 | ||
|
|
7a3ffcbae6 | ||
|
|
95d8783852 | ||
|
|
b4f3604584 | ||
|
|
badccac90c | ||
|
|
1b8d1a5a8d | ||
|
|
2fe57d2597 |
@@ -83,7 +83,8 @@ android test:
|
||||
test_reproducible:
|
||||
stage: check_reproducibility
|
||||
script:
|
||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]='briar ${CI_COMMIT_REF_NAME}' https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||
- "curl -X POST -F token=${RELEASE_JAR_CHECK_TOKEN} -F ref=main -F variables[RELEASE_TAG]='briar-headless ${CI_COMMIT_REF_NAME}' https://code.briarproject.org/api/v4/projects/307/trigger/pipeline"
|
||||
only:
|
||||
- tags
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 31
|
||||
versionCode 10418
|
||||
versionName "1.4.18"
|
||||
versionCode 10421
|
||||
versionName "1.4.21"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.briarproject.bramble">
|
||||
|
||||
<uses-feature
|
||||
@@ -7,15 +8,17 @@
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30" />
|
||||
android:maxSdkVersion="31" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
android:usesPermissionFlags="neverForLocation"
|
||||
tools:targetApi="31" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@@ -118,6 +118,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
try {
|
||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||
boolean connected = net != null && net.isConnected();
|
||||
// Research into Android's behavior to check network connectivity
|
||||
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19)
|
||||
// has shown that NetworkInfo#isConnected() returns true if the device
|
||||
// is connected to any Wifi, independent of whether any specific IP
|
||||
// address can be reached using it or any domain names can be resolved.
|
||||
boolean wifi = false, ipv6Only = false;
|
||||
if (connected) {
|
||||
wifi = net.getType() == TYPE_WIFI;
|
||||
|
||||
@@ -65,6 +65,9 @@ public class AndroidUtils {
|
||||
|
||||
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
// If we don't have permission to access the adapter's address, let
|
||||
// the caller know we can't find it
|
||||
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
|
||||
// Return the adapter's address if it's valid and not fake
|
||||
@SuppressLint("HardwareIds")
|
||||
String address = adapter.getAddress();
|
||||
|
||||
@@ -26,7 +26,7 @@ dependencyVerification {
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
|
||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
||||
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a',
|
||||
'org.briarproject:tor-android:0.4.7.13:tor-android-0.4.7.13.jar:7852aab7d2298b80878c7719f34ce665725b494d673ecf2e6f9e697564638cc6',
|
||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||
|
||||
@Immutable
|
||||
@@ -23,17 +24,24 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
|
||||
public abstract class BdfMessageValidator implements MessageValidator {
|
||||
|
||||
protected static final Logger LOG =
|
||||
Logger.getLogger(BdfMessageValidator.class.getName());
|
||||
getLogger(BdfMessageValidator.class.getName());
|
||||
|
||||
protected final ClientHelper clientHelper;
|
||||
protected final MetadataEncoder metadataEncoder;
|
||||
protected final Clock clock;
|
||||
protected final boolean canonical;
|
||||
|
||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
MetadataEncoder metadataEncoder, Clock clock, boolean canonical) {
|
||||
this.clientHelper = clientHelper;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.clock = clock;
|
||||
this.canonical = canonical;
|
||||
}
|
||||
|
||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
this(clientHelper, metadataEncoder, clock, true);
|
||||
}
|
||||
|
||||
protected abstract BdfMessageContext validateMessage(Message m, Group g,
|
||||
@@ -49,7 +57,7 @@ public abstract class BdfMessageValidator implements MessageValidator {
|
||||
"Timestamp is too far in the future");
|
||||
}
|
||||
try {
|
||||
BdfList bodyList = clientHelper.toList(m.getBody());
|
||||
BdfList bodyList = clientHelper.toList(m, canonical);
|
||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||
return new MessageContext(meta, result.getDependencies());
|
||||
|
||||
@@ -49,6 +49,9 @@ public interface ClientHelper {
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m, boolean canonical)
|
||||
throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
||||
FormatException;
|
||||
|
||||
@@ -106,6 +109,8 @@ public interface ClientHelper {
|
||||
|
||||
BdfList toList(Message m) throws FormatException;
|
||||
|
||||
BdfList toList(Message m, boolean canonical) throws FormatException;
|
||||
|
||||
BdfList toList(Author a);
|
||||
|
||||
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
public class BdfDictionary extends TreeMap<String, Object> {
|
||||
public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
|
||||
public static final Object NULL_VALUE = new Object();
|
||||
|
||||
@@ -39,9 +39,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -52,19 +52,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
public Boolean getBoolean(String key, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Long getLong(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
throw new FormatException();
|
||||
Long value = getOptionalLong(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -78,20 +75,37 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
public Long getLong(String key, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(String key) throws FormatException {
|
||||
Integer value = getOptionalInt(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOptionalInt(String key) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
if (value == null) return null;
|
||||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
public Integer getInt(String key, Integer defaultValue)
|
||||
throws FormatException {
|
||||
Integer value = getOptionalInt(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Double getDouble(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
Double value = getOptionalDouble(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -103,17 +117,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(String key, Double defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
public Double getDouble(String key, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public String getString(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
String value = getOptionalString(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -124,17 +137,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
public String getString(String key, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
byte[] value = getOptionalRaw(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -146,17 +158,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key, byte[] defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
public byte[] getRaw(String key, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfList getList(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
BdfList value = getOptionalList(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -167,16 +178,16 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(String key, BdfList defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
public BdfList getList(String key, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key) throws FormatException {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -188,9 +199,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
|
||||
@NotThreadSafe
|
||||
public class BdfList extends ArrayList<Object> {
|
||||
public final class BdfList extends ArrayList<Object> {
|
||||
|
||||
/**
|
||||
* Factory method for constructing lists inline.
|
||||
@@ -33,15 +33,15 @@ public class BdfList extends ArrayList<Object> {
|
||||
super(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean isInRange(int index) {
|
||||
return index >= 0 && index < size();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -53,21 +53,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index, Boolean defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
public Boolean getBoolean(int index, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Long getLong(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
throw new FormatException();
|
||||
Long value = getOptionalLong(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -82,22 +77,37 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(int index, Long defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
public Long getLong(int index, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(int index) throws FormatException {
|
||||
Integer value = getOptionalInt(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOptionalInt(int index) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
if (value == null) return null;
|
||||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
public Integer getInt(int index, Integer defaultValue)
|
||||
throws FormatException {
|
||||
Integer value = getOptionalInt(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Double getDouble(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
Double value = getOptionalDouble(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -110,19 +120,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(int index, Double defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
public Double getDouble(int index, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public String getString(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
String value = getOptionalString(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -134,19 +141,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(int index, String defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
public String getString(int index, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
byte[] value = getOptionalRaw(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -159,19 +163,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index, byte[] defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
public byte[] getRaw(int index, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfList getList(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
BdfList value = getOptionalList(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -183,18 +184,16 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(int index, BdfList defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
public BdfList getList(int index, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index) throws FormatException {
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -207,10 +206,9 @@ public class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,12 @@ public interface BdfReader {
|
||||
|
||||
void skipLong() throws IOException;
|
||||
|
||||
boolean hasInt() throws IOException;
|
||||
|
||||
int readInt() throws IOException;
|
||||
|
||||
void skipInt() throws IOException;
|
||||
|
||||
boolean hasDouble() throws IOException;
|
||||
|
||||
double readDouble() throws IOException;
|
||||
@@ -54,23 +60,11 @@ public interface BdfReader {
|
||||
|
||||
BdfList readList() throws IOException;
|
||||
|
||||
void readListStart() throws IOException;
|
||||
|
||||
boolean hasListEnd() throws IOException;
|
||||
|
||||
void readListEnd() throws IOException;
|
||||
|
||||
void skipList() throws IOException;
|
||||
|
||||
boolean hasDictionary() throws IOException;
|
||||
|
||||
BdfDictionary readDictionary() throws IOException;
|
||||
|
||||
void readDictionaryStart() throws IOException;
|
||||
|
||||
boolean hasDictionaryEnd() throws IOException;
|
||||
|
||||
void readDictionaryEnd() throws IOException;
|
||||
|
||||
void skipDictionary() throws IOException;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ public interface BdfReaderFactory {
|
||||
|
||||
BdfReader createReader(InputStream in);
|
||||
|
||||
BdfReader createReader(InputStream in, boolean canonical);
|
||||
|
||||
BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize);
|
||||
int maxBufferSize, boolean canonical);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,5 @@ public interface BdfWriter {
|
||||
|
||||
void writeList(Collection<?> c) throws IOException;
|
||||
|
||||
void writeListStart() throws IOException;
|
||||
|
||||
void writeListEnd() throws IOException;
|
||||
|
||||
void writeDictionary(Map<?, ?> m) throws IOException;
|
||||
|
||||
void writeDictionaryStart() throws IOException;
|
||||
|
||||
void writeDictionaryEnd() throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package org.briarproject.bramble.api.keyagreement;
|
||||
|
||||
public interface KeyAgreementConstants {
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
|
||||
/**
|
||||
* The version of the BQP protocol used in beta releases. This version
|
||||
* number is reserved.
|
||||
*/
|
||||
byte BETA_PROTOCOL_VERSION = 89;
|
||||
public interface KeyAgreementConstants {
|
||||
|
||||
/**
|
||||
* The current version of the BQP protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 4;
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish BQP QR codes from
|
||||
* QR codes used for other purposes. See
|
||||
* {@link MailboxConstants#QR_FORMAT_ID}.
|
||||
*/
|
||||
byte QR_FORMAT_ID = 0;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = PROTOCOL_VERSION;
|
||||
|
||||
/**
|
||||
* The length of the BQP key commitment in bytes.
|
||||
*/
|
||||
|
||||
@@ -7,5 +7,5 @@ import java.io.IOException;
|
||||
@NotNullByDefault
|
||||
public interface PayloadParser {
|
||||
|
||||
Payload parse(byte[] raw) throws IOException;
|
||||
Payload parse(String payload) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,6 +20,18 @@ public interface MailboxConstants {
|
||||
*/
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish mailbox QR codes
|
||||
* from QR codes used for other purposes. See
|
||||
* {@link KeyAgreementConstants#QR_FORMAT_ID};
|
||||
*/
|
||||
byte QR_FORMAT_ID = 1;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Mailbox API versions that we support as a client. This is reported to our
|
||||
* contacts by {@link MailboxUpdateManager}.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -34,6 +35,16 @@ public interface MailboxManager {
|
||||
*/
|
||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
||||
|
||||
/**
|
||||
* Takes a textual QR code representation in
|
||||
* {@link org.briarproject.bramble.util.Base32} format and converts it
|
||||
* into a qrCodePayload as expected by {@link #startPairingTask(String)}.
|
||||
*
|
||||
* @throws FormatException when the provided payload did not include a
|
||||
* proper briar-mailbox:// link.
|
||||
*/
|
||||
String convertBase32Payload(String base32Payload) throws FormatException;
|
||||
|
||||
/**
|
||||
* Can be used by the UI to test the mailbox connection.
|
||||
*
|
||||
|
||||
@@ -1,17 +1,44 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
|
||||
public abstract class MailboxPairingState {
|
||||
|
||||
public static class QrCodeReceived extends MailboxPairingState {
|
||||
public abstract static class Pending extends MailboxPairingState {
|
||||
|
||||
public final long timeStarted;
|
||||
|
||||
private Pending(long timeStarted) {
|
||||
this.timeStarted = timeStarted;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pairing extends MailboxPairingState {
|
||||
public static class QrCodeReceived extends Pending {
|
||||
|
||||
public QrCodeReceived(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pairing extends Pending {
|
||||
|
||||
public Pairing(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Paired extends MailboxPairingState {
|
||||
}
|
||||
|
||||
public static class InvalidQrCode extends MailboxPairingState {
|
||||
|
||||
public final QrCodeType qrCodeType;
|
||||
public final int formatVersion;
|
||||
|
||||
public InvalidQrCode(QrCodeType qrCodeType, int formatVersion) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
this.formatVersion = formatVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
||||
|
||||
@@ -27,8 +27,6 @@ public interface TorConstants {
|
||||
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
||||
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
|
||||
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
||||
// TODO: Remove when settings migration code is removed
|
||||
int PREF_TOR_NETWORK_NEVER = 3;
|
||||
|
||||
// Default values for local settings
|
||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface QrCodeClassifier {
|
||||
|
||||
enum QrCodeType {
|
||||
BQP,
|
||||
MAILBOX,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
Pair<QrCodeType, Integer> classifyQrCode(String payload);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Thrown when a QR code that has been scanned does not have the expected type.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class WrongQrCodeTypeException extends FormatException {
|
||||
|
||||
private final QrCodeType qrCodeType;
|
||||
|
||||
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
}
|
||||
|
||||
public QrCodeType getQrCodeType() {
|
||||
return qrCodeType;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -15,15 +14,21 @@ import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.nio.charset.CodingErrorAction.IGNORE;
|
||||
import static java.nio.charset.CodingErrorAction.REPORT;
|
||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
@NotNullByDefault
|
||||
public class StringUtils {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
private static final Pattern MAC =
|
||||
Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
|
||||
private static final char[] HEX = new char[] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
@@ -45,33 +50,41 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static byte[] toUtf8(String s) {
|
||||
try {
|
||||
return s.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return s.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
public static String fromUtf8(byte[] bytes) {
|
||||
return fromUtf8(bytes, 0, bytes.length);
|
||||
public static String fromUtf8(byte[] bytes) throws FormatException {
|
||||
return fromUtf8(bytes, 0, bytes.length, true);
|
||||
}
|
||||
|
||||
public static String fromUtf8(byte[] bytes, int off, int len) {
|
||||
public static String fromUtf8(byte[] bytes, int off, int len)
|
||||
throws FormatException {
|
||||
return fromUtf8(bytes, off, len, true);
|
||||
}
|
||||
|
||||
private static String fromUtf8(byte[] bytes, int off, int len,
|
||||
boolean strict) throws FormatException {
|
||||
CharsetDecoder decoder = UTF_8.newDecoder();
|
||||
decoder.onMalformedInput(IGNORE);
|
||||
decoder.onUnmappableCharacter(IGNORE);
|
||||
decoder.onMalformedInput(strict ? REPORT : IGNORE);
|
||||
decoder.onUnmappableCharacter(strict ? REPORT : IGNORE);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes, off, len);
|
||||
try {
|
||||
return decoder.decode(buffer).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new AssertionError(e);
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public static String truncateUtf8(String s, int maxUtf8Length) {
|
||||
byte[] utf8 = toUtf8(s);
|
||||
if (utf8.length <= maxUtf8Length) return s;
|
||||
return fromUtf8(utf8, 0, maxUtf8Length);
|
||||
// Don't be strict when converting back, so that if we truncate a
|
||||
// multi-byte character the whole character gets dropped
|
||||
try {
|
||||
return fromUtf8(utf8, 0, maxUtf8Length, false);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -8,9 +9,12 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BdfDictionaryTest extends BrambleTestCase {
|
||||
@@ -19,20 +23,20 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
public void testConstructors() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
new BdfDictionary());
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(Collections.singletonMap("foo", NULL_VALUE)));
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(singletonMap("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
BdfDictionary.of());
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
public void testLongPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("foo", (byte) 1);
|
||||
d.put("bar", (short) 2);
|
||||
@@ -44,6 +48,33 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
assertEquals(Long.valueOf(4), d.getLong("bam"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntPromotionAndDemotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("foo", (byte) 1);
|
||||
d.put("bar", (short) 2);
|
||||
d.put("baz", 3);
|
||||
d.put("bam", 4L);
|
||||
assertEquals(Integer.valueOf(1), d.getInt("foo"));
|
||||
assertEquals(Integer.valueOf(2), d.getInt("bar"));
|
||||
assertEquals(Integer.valueOf(3), d.getInt("baz"));
|
||||
assertEquals(Integer.valueOf(4), d.getInt("bam"));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntUnderflow() throws Exception {
|
||||
BdfDictionary d =
|
||||
BdfDictionary.of(new BdfEntry("foo", Integer.MIN_VALUE - 1L));
|
||||
d.getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntOverflow() throws Exception {
|
||||
BdfDictionary d =
|
||||
BdfDictionary.of(new BdfEntry("foo", Integer.MAX_VALUE + 1L));
|
||||
d.getInt("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
@@ -67,7 +98,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeySetIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testKeySetIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -86,7 +117,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValuesIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testValuesIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -105,7 +136,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntrySetIteratorIsOrderedByKeys() throws Exception {
|
||||
public void testEntrySetIteratorIsOrderedByKeys() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -130,4 +161,284 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
assertEquals("d", e.getKey());
|
||||
assertEquals(4, e.getValue());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForIntThrowsFormatException() throws Exception {
|
||||
new BdfDictionary().getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForRawThrowsFormatException() throws Exception {
|
||||
new BdfDictionary().getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForMissingValue()
|
||||
throws Exception {
|
||||
testOptionalMethodsReturnNull(new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||
testOptionalMethodsReturnNull(d);
|
||||
}
|
||||
|
||||
private void testOptionalMethodsReturnNull(BdfDictionary d)
|
||||
throws Exception {
|
||||
assertNull(d.getOptionalBoolean("foo"));
|
||||
assertNull(d.getOptionalLong("foo"));
|
||||
assertNull(d.getOptionalInt("foo"));
|
||||
assertNull(d.getOptionalDouble("foo"));
|
||||
assertNull(d.getOptionalString("foo"));
|
||||
assertNull(d.getOptionalRaw("foo"));
|
||||
assertNull(d.getOptionalList("foo"));
|
||||
assertNull(d.getOptionalDictionary("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForMissingValue()
|
||||
throws Exception {
|
||||
testDefaultMethodsReturnDefault(new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||
testDefaultMethodsReturnDefault(d);
|
||||
}
|
||||
|
||||
private void testDefaultMethodsReturnDefault(BdfDictionary d)
|
||||
throws Exception {
|
||||
assertEquals(TRUE, d.getBoolean("foo", TRUE));
|
||||
assertEquals(Long.valueOf(123L), d.getLong("foo", 123L));
|
||||
assertEquals(Integer.valueOf(123), d.getInt("foo", 123));
|
||||
assertEquals(Double.valueOf(123D), d.getDouble("foo", 123D));
|
||||
assertEquals("123", d.getString("foo", "123"));
|
||||
byte[] defaultRaw = {1, 2, 3};
|
||||
assertArrayEquals(defaultRaw, d.getRaw("foo", defaultRaw));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, d.getList("foo", defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||
assertEquals(defaultDict, d.getDictionary("foo", defaultDict));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo", true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo", 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo", 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo", 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo", "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo", new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo",
|
||||
new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo",
|
||||
new BdfDictionary());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,31 +5,31 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class BdfListTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testConstructors() {
|
||||
assertEquals(Collections.emptyList(), new BdfList());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
new BdfList(Arrays.asList(1, 2, NULL_VALUE)));
|
||||
assertEquals(emptyList(), new BdfList());
|
||||
assertEquals(asList(1, 2, NULL_VALUE),
|
||||
new BdfList(asList(1, 2, NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(Collections.emptyList(), BdfList.of());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
BdfList.of(1, 2, NULL_VALUE));
|
||||
assertEquals(emptyList(), BdfList.of());
|
||||
assertEquals(asList(1, 2, NULL_VALUE), BdfList.of(1, 2, NULL_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
public void testLongPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
list.add((byte) 1);
|
||||
list.add((short) 2);
|
||||
@@ -41,6 +41,31 @@ public class BdfListTest extends BrambleTestCase {
|
||||
assertEquals(Long.valueOf(4), list.getLong(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntPromotionAndDemotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
list.add((byte) 1);
|
||||
list.add((short) 2);
|
||||
list.add(3);
|
||||
list.add(4L);
|
||||
assertEquals(Integer.valueOf(1), list.getInt(0));
|
||||
assertEquals(Integer.valueOf(2), list.getInt(1));
|
||||
assertEquals(Integer.valueOf(3), list.getInt(2));
|
||||
assertEquals(Integer.valueOf(4), list.getInt(3));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntUnderflow() throws Exception {
|
||||
BdfList list = BdfList.of(Integer.MIN_VALUE - 1L);
|
||||
list.getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntOverflow() throws Exception {
|
||||
BdfList list = BdfList.of(Integer.MAX_VALUE + 1L);
|
||||
list.getInt(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
@@ -63,61 +88,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
assertArrayEquals(new byte[123], second);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void testIndexOutOfBoundsReturnsDefaultValue() throws Exception {
|
||||
BdfList list = BdfList.of(1, 2, 3);
|
||||
boolean defaultBoolean = true;
|
||||
assertEquals(defaultBoolean, list.getBoolean(-1, defaultBoolean));
|
||||
assertEquals(defaultBoolean, list.getBoolean(3, defaultBoolean));
|
||||
Long defaultLong = 123L;
|
||||
assertEquals(defaultLong, list.getLong(-1, defaultLong));
|
||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
||||
Double defaultDouble = 1.23;
|
||||
assertEquals(defaultDouble, list.getDouble(-1, defaultDouble));
|
||||
assertEquals(defaultDouble, list.getDouble(3, defaultDouble));
|
||||
String defaultString = "123";
|
||||
assertEquals(defaultString, list.getString(-1, defaultString));
|
||||
assertEquals(defaultString, list.getString(3, defaultString));
|
||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
||||
assertArrayEquals(defaultBytes, list.getRaw(-1, defaultBytes));
|
||||
assertArrayEquals(defaultBytes, list.getRaw(3, defaultBytes));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(-1, defaultList));
|
||||
assertEquals(defaultList, list.getList(3, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(
|
||||
new BdfEntry("1", 1),
|
||||
new BdfEntry("2", 2),
|
||||
new BdfEntry("3", 3)
|
||||
);
|
||||
assertEquals(defaultDict, list.getDictionary(-1, defaultDict));
|
||||
assertEquals(defaultDict, list.getDictionary(3, defaultDict));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void testWrongTypeReturnsDefaultValue() throws Exception {
|
||||
BdfList list = BdfList.of(1, 2, 3, true);
|
||||
boolean defaultBoolean = true;
|
||||
assertEquals(defaultBoolean, list.getBoolean(0, defaultBoolean));
|
||||
Long defaultLong = 123L;
|
||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
||||
Double defaultDouble = 1.23;
|
||||
assertEquals(defaultDouble, list.getDouble(0, defaultDouble));
|
||||
String defaultString = "123";
|
||||
assertEquals(defaultString, list.getString(0, defaultString));
|
||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
||||
assertArrayEquals(defaultBytes, list.getRaw(0, defaultBytes));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(0, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(
|
||||
new BdfEntry("1", 1),
|
||||
new BdfEntry("2", 2),
|
||||
new BdfEntry("3", 3)
|
||||
);
|
||||
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -130,6 +100,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(-1, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -142,6 +118,30 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalLong(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getLong(-1, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getOptionalInt(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(-1, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -154,6 +154,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(-1, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -166,6 +172,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(-1, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -178,6 +190,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(-1, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -190,6 +208,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(-1, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDictionaryThrowsFormatException()
|
||||
@@ -203,6 +226,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDictionary(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(-1, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -215,6 +244,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -227,6 +262,30 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getLong(0, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getOptionalInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(0, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -239,6 +298,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -251,6 +316,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -263,6 +334,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(0, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -275,6 +352,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(0, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDictionaryThrowsFormatException()
|
||||
@@ -287,6 +369,13 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
new BdfList().getOptionalDictionary(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(0, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -299,6 +388,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||
BdfList.of(1.23).getLong(0);
|
||||
@@ -310,6 +405,29 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(1.23).getOptionalLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getLong(0, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||
BdfList.of(1.23).getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getOptionalInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getInt(0, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getDouble(0);
|
||||
@@ -321,6 +439,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getString(0);
|
||||
@@ -332,6 +456,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getRaw(0);
|
||||
@@ -343,6 +473,12 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getRaw(0, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForListThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getList(0);
|
||||
@@ -354,6 +490,11 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getList(0, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||
@@ -366,4 +507,81 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
BdfList.of(123).getOptionalDictionary(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getDictionary(0, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(NULL_VALUE).getBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(NULL_VALUE).getDictionary(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||
BdfList list = BdfList.of(NULL_VALUE);
|
||||
assertNull(list.getOptionalBoolean(0));
|
||||
assertNull(list.getOptionalLong(0));
|
||||
assertNull(list.getOptionalInt(0));
|
||||
assertNull(list.getOptionalDouble(0));
|
||||
assertNull(list.getOptionalString(0));
|
||||
assertNull(list.getOptionalRaw(0));
|
||||
assertNull(list.getOptionalList(0));
|
||||
assertNull(list.getOptionalDictionary(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||
BdfList list = BdfList.of(NULL_VALUE);
|
||||
assertEquals(TRUE, list.getBoolean(0, TRUE));
|
||||
assertEquals(Long.valueOf(123L), list.getLong(0, 123L));
|
||||
assertEquals(Integer.valueOf(123), list.getInt(0, 123));
|
||||
assertEquals(Double.valueOf(123D), list.getDouble(0, 123D));
|
||||
assertEquals("123", list.getString(0, "123"));
|
||||
byte[] defaultRaw = {1, 2, 3};
|
||||
assertArrayEquals(defaultRaw, list.getRaw(0, defaultRaw));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(0, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ dependencies {
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
|
||||
|
||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
||||
import org.briarproject.bramble.plugin.PluginModule;
|
||||
import org.briarproject.bramble.properties.PropertiesModule;
|
||||
import org.briarproject.bramble.qrcode.QrCodeModule;
|
||||
import org.briarproject.bramble.record.RecordModule;
|
||||
import org.briarproject.bramble.reliability.ReliabilityModule;
|
||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||
@@ -47,6 +48,7 @@ import dagger.Module;
|
||||
MailboxModule.class,
|
||||
PluginModule.class,
|
||||
PropertiesModule.class,
|
||||
QrCodeModule.class,
|
||||
RecordModule.class,
|
||||
ReliabilityModule.class,
|
||||
RendezvousModule.class,
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -29,6 +28,7 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
|
||||
@@ -99,7 +99,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
new FileInputStream(f), UTF_8));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
@@ -151,7 +151,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
@GuardedBy("stateChangeLock")
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes(Charset.forName("UTF-8")));
|
||||
out.write(key.getBytes(UTF_8));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@@ -155,7 +155,13 @@ class ClientHelperImpl implements ClientHelper {
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException {
|
||||
return toList(db.getMessage(txn, m).getBody());
|
||||
return getMessageAsList(txn, m, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m,
|
||||
boolean canonical) throws DbException, FormatException {
|
||||
return toList(db.getMessage(txn, m), canonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -313,8 +319,13 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
||||
return toList(b, off, len, true);
|
||||
}
|
||||
|
||||
private BdfList toList(byte[] b, int off, int len, boolean canonical)
|
||||
throws FormatException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in, canonical);
|
||||
try {
|
||||
BdfList list = reader.readList();
|
||||
if (!reader.eof()) throw new FormatException();
|
||||
@@ -328,7 +339,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b) throws FormatException {
|
||||
return toList(b, 0, b.length);
|
||||
return toList(b, 0, b.length, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,6 +347,12 @@ class ClientHelperImpl implements ClientHelper {
|
||||
return toList(m.getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList toList(Message m, boolean canonical) throws FormatException {
|
||||
byte[] b = m.getBody();
|
||||
return toList(b, 0, b.length, canonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList toList(Author a) {
|
||||
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
||||
@@ -361,7 +378,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
public Author parseAndValidateAuthor(BdfList author)
|
||||
throws FormatException {
|
||||
checkSize(author, 3);
|
||||
int formatVersion = author.getLong(0).intValue();
|
||||
int formatVersion = author.getInt(0);
|
||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||
String name = author.getString(1);
|
||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
@@ -472,8 +489,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
if (element.size() != 2) {
|
||||
throw new FormatException();
|
||||
}
|
||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
||||
element.getLong(1).intValue()));
|
||||
list.add(new MailboxVersion(element.getInt(0), element.getInt(1)));
|
||||
}
|
||||
// Sort the list of versions for easier comparison
|
||||
sort(list);
|
||||
@@ -486,7 +502,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
getGroupMetadataAsDictionary(txn, contactGroupId);
|
||||
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
|
||||
return new ContactId(meta.getInt(GROUP_KEY_CONTACT_ID));
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
@@ -51,6 +50,7 @@ import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHE
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
@NotNullByDefault
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
@@ -460,7 +460,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
@Override
|
||||
public String encodeOnion(byte[] publicKey) {
|
||||
Digest digest = new SHA3Digest(256);
|
||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
||||
byte[] label = ".onion checksum".getBytes(US_ASCII);
|
||||
digest.update(label, 0, label.length);
|
||||
digest.update(publicKey, 0, publicKey.length);
|
||||
digest.update(ONION_HS_PROTOCOL_VERSION);
|
||||
|
||||
@@ -39,12 +39,13 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Scanner;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MessageEncrypter {
|
||||
@@ -228,7 +229,7 @@ public class MessageEncrypter {
|
||||
PublicKey publicKey =
|
||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||
String message = readFully(System.in);
|
||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
||||
byte[] plaintext = message.getBytes(UTF_8);
|
||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||
}
|
||||
@@ -242,7 +243,7 @@ public class MessageEncrypter {
|
||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
||||
System.out.println(new String(plaintext, UTF_8));
|
||||
}
|
||||
|
||||
private static String readFully(InputStream in) throws IOException {
|
||||
|
||||
@@ -18,12 +18,18 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in) {
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in, boolean canonical) {
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE, canonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
|
||||
int maxBufferSize, boolean canonical) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize, canonical);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,21 +33,24 @@ import static org.briarproject.bramble.util.StringUtils.fromUtf8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class BdfReaderImpl implements BdfReader {
|
||||
final class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||
|
||||
private final InputStream in;
|
||||
private final int nestedLimit, maxBufferSize;
|
||||
private final boolean canonical;
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next;
|
||||
private byte[] buf = new byte[8];
|
||||
|
||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
|
||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize,
|
||||
boolean canonical) {
|
||||
this.in = in;
|
||||
this.nestedLimit = nestedLimit;
|
||||
this.maxBufferSize = maxBufferSize;
|
||||
this.canonical = canonical;
|
||||
}
|
||||
|
||||
private void readLookahead() throws IOException {
|
||||
@@ -188,13 +191,22 @@ class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private short readInt16() throws IOException {
|
||||
readIntoBuffer(2);
|
||||
return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||
short value = (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||
if (canonical && value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_8
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private int readInt32() throws IOException {
|
||||
readIntoBuffer(4);
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
||||
if (canonical && value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_16
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -202,6 +214,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
readIntoBuffer(8);
|
||||
long value = 0;
|
||||
for (int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||
if (canonical && value >= Integer.MIN_VALUE &&
|
||||
value <= Integer.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_32
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -215,6 +232,31 @@ class BdfReaderImpl implements BdfReader {
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInt() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
if (eof) return false;
|
||||
return next == INT_8 || next == INT_16 || next == INT_32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
if (!hasInt()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
if (next == INT_8) return readInt8();
|
||||
if (next == INT_16) return readInt16();
|
||||
return readInt32();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipInt() throws IOException {
|
||||
if (!hasInt()) throw new FormatException();
|
||||
if (next == INT_8) skip(1);
|
||||
else if (next == INT_16) skip(2);
|
||||
else skip(4);
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDouble() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
@@ -323,22 +365,11 @@ class BdfReaderImpl implements BdfReader {
|
||||
private BdfList readList(int level) throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
if (level > nestedLimit) throw new FormatException();
|
||||
BdfList list = new BdfList();
|
||||
readListStart();
|
||||
while (!hasListEnd()) list.add(readObject(level + 1));
|
||||
readListEnd();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListStart() throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasListEnd() throws IOException {
|
||||
return hasEnd();
|
||||
BdfList list = new BdfList();
|
||||
while (!hasEnd()) list.add(readObject(level + 1));
|
||||
readEnd();
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
@@ -347,11 +378,6 @@ class BdfReaderImpl implements BdfReader {
|
||||
return next == END;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if (!hasEnd()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
@@ -361,7 +387,7 @@ class BdfReaderImpl implements BdfReader {
|
||||
public void skipList() throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasListEnd()) skipObject();
|
||||
while (!hasEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@@ -380,35 +406,27 @@ class BdfReaderImpl implements BdfReader {
|
||||
private BdfDictionary readDictionary(int level) throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
if (level > nestedLimit) throw new FormatException();
|
||||
BdfDictionary dictionary = new BdfDictionary();
|
||||
readDictionaryStart();
|
||||
while (!hasDictionaryEnd())
|
||||
dictionary.put(readString(), readObject(level + 1));
|
||||
readDictionaryEnd();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDictionaryStart() throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDictionaryEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDictionaryEnd() throws IOException {
|
||||
BdfDictionary dictionary = new BdfDictionary();
|
||||
String prevKey = null;
|
||||
while (!hasEnd()) {
|
||||
String key = readString();
|
||||
if (canonical && prevKey != null && key.compareTo(prevKey) <= 0) {
|
||||
// Keys not unique and sorted
|
||||
throw new FormatException();
|
||||
}
|
||||
dictionary.put(key, readObject(level + 1));
|
||||
prevKey = key;
|
||||
}
|
||||
readEnd();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipDictionary() throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasDictionaryEnd()) {
|
||||
while (!hasEnd()) {
|
||||
skipString();
|
||||
skipObject();
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -15,6 +17,7 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import static java.util.Collections.sort;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.data.Types.DICTIONARY;
|
||||
import static org.briarproject.bramble.data.Types.END;
|
||||
@@ -33,10 +36,11 @@ import static org.briarproject.bramble.data.Types.STRING_16;
|
||||
import static org.briarproject.bramble.data.Types.STRING_32;
|
||||
import static org.briarproject.bramble.data.Types.STRING_8;
|
||||
import static org.briarproject.bramble.data.Types.TRUE;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class BdfWriterImpl implements BdfWriter {
|
||||
final class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
@@ -113,7 +117,7 @@ class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
@Override
|
||||
public void writeString(String s) throws IOException {
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
byte[] b = s.getBytes(UTF_8);
|
||||
if (b.length <= Byte.MAX_VALUE) {
|
||||
out.write(STRING_8);
|
||||
out.write((byte) b.length);
|
||||
@@ -161,39 +165,33 @@ class BdfWriterImpl implements BdfWriter {
|
||||
else if (o instanceof String) writeString((String) o);
|
||||
else if (o instanceof byte[]) writeRaw((byte[]) o);
|
||||
else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
|
||||
else if (o instanceof List) writeList((List) o);
|
||||
else if (o instanceof Map) writeDictionary((Map) o);
|
||||
else if (o instanceof List) writeList((List<?>) o);
|
||||
else if (o instanceof Map) writeDictionary((Map<?, ?>) o);
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListStart() throws IOException {
|
||||
out.write(LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionary(Map<?, ?> m) throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
for (Entry<?, ?> e : m.entrySet()) {
|
||||
if (!(e.getKey() instanceof String)) throw new FormatException();
|
||||
writeString((String) e.getKey());
|
||||
writeObject(e.getValue());
|
||||
if (m instanceof BdfDictionary) {
|
||||
// Entries are already sorted and keys are known to be strings
|
||||
for (Entry<String, Object> e : ((BdfDictionary) m).entrySet()) {
|
||||
writeString(e.getKey());
|
||||
writeObject(e.getValue());
|
||||
}
|
||||
} else {
|
||||
// Check that keys are strings, write entries in canonical order
|
||||
List<String> keys = new ArrayList<>(m.size());
|
||||
for (Object k : m.keySet()) {
|
||||
if (!(k instanceof String)) throw new FormatException();
|
||||
keys.add((String) k);
|
||||
}
|
||||
sort(keys);
|
||||
for (String key : keys) {
|
||||
writeString(key);
|
||||
writeObject(m.get(key));
|
||||
}
|
||||
}
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryStart() throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
@@ -19,13 +17,13 @@ public class KeyAgreementModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadEncoder providePayloadEncoder(BdfWriterFactory bdfWriterFactory) {
|
||||
return new PayloadEncoderImpl(bdfWriterFactory);
|
||||
PayloadEncoder providePayloadEncoder(PayloadEncoderImpl payloadEncoder) {
|
||||
return payloadEncoder;
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadParser providePayloadParser(BdfReaderFactory bdfReaderFactory) {
|
||||
return new PayloadParserImpl(bdfReaderFactory);
|
||||
PayloadParser providePayloadParser(PayloadParserImpl payloadParser) {
|
||||
return payloadParser;
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
@@ -13,7 +14,8 @@ import java.io.IOException;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_ID;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -29,14 +31,16 @@ class PayloadEncoderImpl implements PayloadEncoder {
|
||||
@Override
|
||||
public byte[] encode(Payload p) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(PROTOCOL_VERSION);
|
||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||
out.write(formatIdAndVersion);
|
||||
BdfList payload = new BdfList();
|
||||
payload.add(p.getCommitment());
|
||||
for (TransportDescriptor d : p.getTransportDescriptors()) {
|
||||
payload.add(d.getDescriptor());
|
||||
}
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
try {
|
||||
w.writeListStart(); // Payload start
|
||||
w.writeRaw(p.getCommitment());
|
||||
for (TransportDescriptor d : p.getTransportDescriptors())
|
||||
w.writeList(d.getDescriptor());
|
||||
w.writeListEnd(); // Payload end
|
||||
w.writeList(payload);
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen with ByteArrayOutputStream
|
||||
throw new AssertionError(e);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
@@ -11,6 +12,9 @@ import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -21,34 +25,42 @@ import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class PayloadParserImpl implements PayloadParser {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory) {
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload parse(byte[] raw) throws IOException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the protocol version
|
||||
int protocolVersion = in.read();
|
||||
if (protocolVersion == -1) throw new FormatException();
|
||||
if (protocolVersion != PROTOCOL_VERSION) {
|
||||
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
|
||||
protocolVersion == BETA_PROTOCOL_VERSION;
|
||||
public Payload parse(String payloadString) throws IOException {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payloadString);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (formatVersion != QR_FORMAT_VERSION) {
|
||||
boolean tooOld = formatVersion < QR_FORMAT_VERSION;
|
||||
throw new UnsupportedVersionException(tooOld);
|
||||
}
|
||||
byte[] raw = payloadString.getBytes(ISO_8859_1);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the format identifier and version (already parsed)
|
||||
if (in.read() == -1) throw new AssertionError();
|
||||
// The rest of the payload is a BDF list with one or more elements
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
BdfList payload = r.readList();
|
||||
@@ -61,7 +73,7 @@ class PayloadParserImpl implements PayloadParser {
|
||||
List<TransportDescriptor> recognised = new ArrayList<>();
|
||||
for (int i = 1; i < payload.size(); i++) {
|
||||
BdfList descriptor = payload.getList(i);
|
||||
long transportId = descriptor.getLong(0);
|
||||
int transportId = descriptor.getInt(0);
|
||||
if (transportId == TRANSPORT_ID_BLUETOOTH) {
|
||||
TransportId id = BluetoothConstants.ID;
|
||||
recognised.add(new TransportDescriptor(id, descriptor));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
@@ -11,12 +12,15 @@ import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
@@ -26,6 +30,7 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -98,6 +103,22 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertBase32Payload(String base32Payload)
|
||||
throws FormatException {
|
||||
Pattern regex = Pattern.compile("(briar-mailbox://)?([a-z2-7]{104})");
|
||||
Matcher matcher = regex.matcher(base32Payload);
|
||||
if (!matcher.find()) throw new FormatException();
|
||||
String base32 = matcher.group(2);
|
||||
byte[] payloadBytes;
|
||||
try {
|
||||
payloadBytes = Base32.decode(base32, false);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return new String(payloadBytes, ISO_8859_1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConnection() {
|
||||
List<MailboxVersion> versions = null;
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -25,6 +26,7 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
MailboxPairingTaskFactoryImpl(
|
||||
@@ -34,7 +36,8 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
this.crypto = crypto;
|
||||
@@ -42,12 +45,13 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
|
||||
crypto, clock, api, mailboxSettingsManager,
|
||||
mailboxUpdateManager);
|
||||
mailboxUpdateManager, qrCodeClassifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -9,18 +10,26 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -32,7 +41,10 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
@@ -40,9 +52,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
private final static Logger LOG =
|
||||
getLogger(MailboxPairingTaskImpl.class.getName());
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
private static final int VERSION_REQUIRED = 32;
|
||||
|
||||
private final String payload;
|
||||
private final Executor eventExecutor;
|
||||
@@ -52,6 +61,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
private final long timeStarted;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
@@ -68,7 +79,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
this.payload = payload;
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
@@ -77,7 +89,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
state = new MailboxPairingState.QrCodeReceived();
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
timeStarted = clock.currentTimeMillis();
|
||||
state = new QrCodeReceived(timeStarted);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,22 +113,30 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payload);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (qrCodeType != MAILBOX || formatVersion != QR_FORMAT_VERSION) {
|
||||
setState(new InvalidQrCode(qrCodeType, formatVersion));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
pairMailbox();
|
||||
} catch (FormatException e) {
|
||||
onMailboxError(e, new MailboxPairingState.InvalidQrCode());
|
||||
onMailboxError(e, new InvalidQrCode(qrCodeType, formatVersion));
|
||||
} catch (MailboxAlreadyPairedException e) {
|
||||
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
|
||||
onMailboxError(e, new MailboxAlreadyPaired());
|
||||
} catch (IOException e) {
|
||||
onMailboxError(e, new MailboxPairingState.ConnectionError());
|
||||
onMailboxError(e, new ConnectionError());
|
||||
} catch (ApiException | DbException e) {
|
||||
onMailboxError(e, new MailboxPairingState.UnexpectedError());
|
||||
onMailboxError(e, new UnexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
private void pairMailbox() throws IOException, ApiException, DbException {
|
||||
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
|
||||
setState(new MailboxPairingState.Pairing());
|
||||
setState(new Pairing(timeStarted));
|
||||
MailboxProperties ownerProperties = api.setup(mailboxProperties);
|
||||
long time = clock.currentTimeMillis();
|
||||
db.transaction(false, txn -> {
|
||||
@@ -133,7 +155,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
}
|
||||
});
|
||||
setState(new MailboxPairingState.Paired());
|
||||
setState(new Paired());
|
||||
}
|
||||
|
||||
private void onMailboxError(Exception e, MailboxPairingState state) {
|
||||
@@ -167,14 +189,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
int version = bytes[0] & 0xFF;
|
||||
if (version != VERSION_REQUIRED) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("QR code has not version " + VERSION_REQUIRED +
|
||||
": " + version);
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
LOG.info("QR code is valid");
|
||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||
String onion = crypto.encodeOnion(onionPubKey);
|
||||
|
||||
@@ -419,7 +419,7 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
private InetSocketAddress parseSocketAddress(BdfList descriptor)
|
||||
throws FormatException {
|
||||
byte[] address = descriptor.getRaw(1);
|
||||
int port = descriptor.getLong(2).intValue();
|
||||
int port = descriptor.getInt(2);
|
||||
if (port < 1 || port > MAX_16_BIT_UNSIGNED) throw new FormatException();
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByAddress(address);
|
||||
|
||||
@@ -46,7 +46,6 @@ import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -100,6 +99,7 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -140,7 +140,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
private final long maxLatency;
|
||||
private final int maxIdleTime;
|
||||
private final int socketTimeout;
|
||||
private final File torDirectory, geoIpFile, configFile;
|
||||
private final File torDirectory;
|
||||
private final File configFile;
|
||||
private final int torSocksPort;
|
||||
private final int torControlPort;
|
||||
private final File doneFile, cookieFile;
|
||||
@@ -195,7 +196,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
this.torDirectory = torDirectory;
|
||||
this.torSocksPort = torSocksPort;
|
||||
this.torControlPort = torControlPort;
|
||||
geoIpFile = new File(torDirectory, "geoip");
|
||||
configFile = new File(torDirectory, "torrc");
|
||||
doneFile = new File(torDirectory, "done");
|
||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||
@@ -332,12 +332,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
// The done file may already exist from a previous installation
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
doneFile.delete();
|
||||
// The GeoIP file may exist from a previous installation - we can
|
||||
// save some space by deleting it.
|
||||
// TODO: Remove after a reasonable migration period
|
||||
// (added 2022-03-29)
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
geoIpFile.delete();
|
||||
installTorExecutable();
|
||||
installObfs4Executable();
|
||||
installSnowflakeExecutable();
|
||||
@@ -419,9 +413,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new ByteArrayInputStream(
|
||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||
return new ByteArrayInputStream(strb.toString().getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private void listFiles(File f) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
|
||||
@@ -31,6 +31,6 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
||||
byte[] hash = spec.getH();
|
||||
byte[] base64 = Base64.encode(hash);
|
||||
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII"));
|
||||
return "ED25519-V3:" + new String(base64, US_ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
// Retrieve and parse the latest local properties
|
||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
e.getValue().messageId);
|
||||
e.getValue().messageId, false);
|
||||
local.put(e.getKey(), parseProperties(message));
|
||||
}
|
||||
return local;
|
||||
@@ -222,7 +222,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest local properties
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
p = parseProperties(message);
|
||||
}
|
||||
return p == null ? new TransportProperties() : p;
|
||||
@@ -252,7 +252,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
local = new TransportProperties();
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
local = parseProperties(message);
|
||||
}
|
||||
storeLocalProperties(txn, c, t, local);
|
||||
@@ -272,8 +272,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
remote = new TransportProperties();
|
||||
} else {
|
||||
// Retrieve and parse the latest remote properties
|
||||
BdfList message =
|
||||
clientHelper.getMessageAsList(txn, latest.messageId);
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
remote = parseProperties(message);
|
||||
}
|
||||
// Merge in any discovered properties
|
||||
@@ -317,7 +317,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
changed = true;
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId);
|
||||
latest.messageId, false);
|
||||
TransportProperties old = parseProperties(message);
|
||||
merged = new TransportProperties(old);
|
||||
for (Entry<String, String> e : p.entrySet()) {
|
||||
|
||||
@@ -27,7 +27,10 @@ class TransportPropertyValidator extends BdfMessageValidator {
|
||||
|
||||
TransportPropertyValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
// Accept transport properties in non-canonical form
|
||||
// TODO: Remove this after a reasonable migration period
|
||||
// (added 2023-02-17)
|
||||
super(clientHelper, metadataEncoder, clock, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class QrCodeClassifierImpl implements QrCodeClassifier {
|
||||
|
||||
@Inject
|
||||
QrCodeClassifierImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<QrCodeType, Integer> classifyQrCode(String payload) {
|
||||
byte[] bytes = payload.getBytes(ISO_8859_1);
|
||||
if (bytes.length == 0) return new Pair<>(UNKNOWN, 0);
|
||||
// If this is a Bramble QR code then the first byte encodes the
|
||||
// format ID (3 bits) and version (5 bits)
|
||||
int formatIdAndVersion = bytes[0] & 0xFF;
|
||||
int formatId = formatIdAndVersion >> 5;
|
||||
int formatVersion = formatIdAndVersion & 0x1F;
|
||||
if (formatId == KeyAgreementConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(BQP, formatVersion);
|
||||
}
|
||||
if (formatId == MailboxConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(MAILBOX, formatVersion);
|
||||
}
|
||||
return new Pair<>(UNKNOWN, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class QrCodeModule {
|
||||
|
||||
@Provides
|
||||
QrCodeClassifier provideQrCodeClassifier(
|
||||
QrCodeClassifierImpl qrCodeClassifier) {
|
||||
return qrCodeClassifier;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
||||
@@ -126,6 +127,8 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
byte[] payload = nextRecord.getPayload();
|
||||
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||
throw new FormatException();
|
||||
if (payload.length > MAX_MESSAGE_LENGTH)
|
||||
throw new FormatException();
|
||||
// Validate timestamp
|
||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||
if (timestamp < 0) throw new FormatException();
|
||||
|
||||
@@ -32,8 +32,7 @@ class SessionParserImpl implements SessionParser {
|
||||
|
||||
@Override
|
||||
public Session parseSession(BdfDictionary meta) throws FormatException {
|
||||
State state =
|
||||
State.fromValue(meta.getLong(SESSION_KEY_STATE).intValue());
|
||||
State state = State.fromValue(meta.getInt(SESSION_KEY_STATE));
|
||||
|
||||
MessageId lastLocalMessageId = null;
|
||||
byte[] lastLocalMessageIdBytes =
|
||||
@@ -56,9 +55,9 @@ class SessionParserImpl implements SessionParser {
|
||||
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
||||
|
||||
KeySetId keySetId = null;
|
||||
Long keySetIdLong = meta.getOptionalLong(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdLong != null) {
|
||||
keySetId = new KeySetId(keySetIdLong.intValue());
|
||||
Integer keySetIdInt = meta.getOptionalInt(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdInt != null) {
|
||||
keySetId = new KeySetId(keySetIdInt);
|
||||
}
|
||||
|
||||
return new Session(state, lastLocalMessageId, localKeyPair,
|
||||
|
||||
@@ -177,8 +177,8 @@ class TransportKeyAgreementManagerImpl extends BdfIncomingMessageHook
|
||||
protected DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||
BdfList body, BdfDictionary meta)
|
||||
throws DbException, FormatException {
|
||||
MessageType type = MessageType.fromValue(
|
||||
meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
|
||||
MessageType type =
|
||||
MessageType.fromValue(meta.getInt(MSG_KEY_MESSAGE_TYPE));
|
||||
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Received " + type + " message for " + t);
|
||||
|
||||
@@ -42,7 +42,7 @@ class TransportKeyAgreementValidator extends BdfMessageValidator {
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
|
||||
MessageType type = MessageType.fromValue(body.getInt(0));
|
||||
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
|
||||
else if (type == ACTIVATE) return validateActivateMessage(body);
|
||||
else throw new AssertionError();
|
||||
|
||||
@@ -301,8 +301,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
for (int i = 0; i < size; i++) {
|
||||
BdfList cv = body.getList(i);
|
||||
ClientId clientId = new ClientId(cv.getString(0));
|
||||
int majorVersion = cv.getLong(1).intValue();
|
||||
int minorVersion = cv.getLong(2).intValue();
|
||||
int majorVersion = cv.getInt(1);
|
||||
int minorVersion = cv.getInt(2);
|
||||
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion));
|
||||
}
|
||||
return parsed;
|
||||
@@ -408,8 +408,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
throws FormatException {
|
||||
// Client ID, major version, minor version, active
|
||||
ClientId clientId = new ClientId(clientState.getString(0));
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
int majorVersion = clientState.getInt(1);
|
||||
int minorVersion = clientState.getInt(2);
|
||||
boolean active = clientState.getBoolean(3);
|
||||
return new ClientState(clientId, majorVersion, minorVersion, active);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ class ClientVersioningValidator extends BdfMessageValidator {
|
||||
checkSize(clientState, 4);
|
||||
String clientId = clientState.getString(0);
|
||||
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
int majorVersion = clientState.getInt(1);
|
||||
if (majorVersion < 0) throw new FormatException();
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
int minorVersion = clientState.getInt(2);
|
||||
if (minorVersion < 0) throw new FormatException();
|
||||
clientState.getBoolean(3);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=
|
||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
||||
n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0
|
||||
n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0
|
||||
n Bridge obfs4 207.154.242.137:80 8E67A1B2A342652EE27376BD61BECF5806700E7F cert=qUrR9fan3XPNGNOwn9WGlXLJNZZx0grXH4AZXR+yoBbtbbj5Ak1n4a7TtjYgXcWcs/gHXw iat-mode=0
|
||||
n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0
|
||||
n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0
|
||||
n Bridge obfs4 15.235.47.204:42058 869133925B3CD07683BDF01805C36448D090CE88 cert=PFwh4mzZlSTUdcEskpe20t998n5jbr81s+XoX7gmazqzUGHNhkendK5K1j2gOxesz9AkBw iat-mode=0
|
||||
n Bridge obfs4 51.75.93.136:45532 8402B84833527BC249B21AC885134197E624FB5A cert=LwXEf/Dgo0tKdMJByXdlvWiJqyyPw4T284Cg5qygDuIJJNFuz3ED9UhGil6H4Of3gM7wSg iat-mode=0
|
||||
n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0
|
||||
n Bridge obfs4 45.150.172.16:80 82849E69CBB25EA7F479155F7DCD89D85717FF47 cert=+Krdu1jmVQOxWkMj0mqJHgwbQV49eyD6mZDS+mRExssWNHosa60g4P5Gp81sBJKzN8NrSw iat-mode=0
|
||||
n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0
|
||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -34,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -342,7 +342,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
private void storeDatabaseKey(File f, String hex) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(hex.getBytes(Charset.forName("UTF-8")));
|
||||
out.write(hex.getBytes(UTF_8));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
@@ -350,7 +350,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
@Nullable
|
||||
private String loadDatabaseKey(File f) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
new FileInputStream(f), UTF_8));
|
||||
String hex = reader.readLine();
|
||||
reader.close();
|
||||
return hex;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -86,7 +86,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(shortMessage.getBody());
|
||||
oneOf(clientHelper).toList(shortMessage, true);
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -114,7 +114,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(throwException(new FormatException()));
|
||||
}});
|
||||
|
||||
@@ -126,7 +126,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
will(returnValue(body));
|
||||
}});
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory)
|
||||
.createReader(with(any(InputStream.class)));
|
||||
.createReader(with(any(InputStream.class)), with(true));
|
||||
will(returnValue(bdfReader));
|
||||
oneOf(bdfReader).readList();
|
||||
will(returnValue(list));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -31,11 +32,14 @@ public class BdfReaderImplFuzzingTest extends BrambleTestCase {
|
||||
buf[1] = 0x14; // Length 20 bytes
|
||||
in.reset();
|
||||
BdfReaderImpl r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
int length = r.readString().length();
|
||||
assertTrue(length >= 0);
|
||||
assertTrue(length <= 20);
|
||||
assertTrue(r.eof());
|
||||
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||
try {
|
||||
int length = r.readString().length();
|
||||
assertTrue(length <= 20);
|
||||
assertTrue(r.eof());
|
||||
} catch (FormatException e) {
|
||||
// Expected when bytes are not valid UTF-8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.io.ByteArrayInputStream;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||
import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
@@ -88,6 +89,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong16CouldHaveBeenLong8Max() throws Exception {
|
||||
setContents("22" + "007F");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong16CouldHaveBeenLong8Min() throws Exception {
|
||||
setContents("22" + "FF80");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipLong16() throws Exception {
|
||||
setContents("22" + "0080");
|
||||
@@ -106,6 +119,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong32CouldHaveBeenLong16Max() throws Exception {
|
||||
setContents("24" + "00007FFF");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong32CouldHaveBeenLong16Min() throws Exception {
|
||||
setContents("24" + "FFFF8000");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipLong32() throws Exception {
|
||||
setContents("24" + "00008000");
|
||||
@@ -124,13 +149,48 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong64CouldHaveBeenLong32Max() throws Exception {
|
||||
setContents("28" + "000000007FFFFFFF");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadLong64CouldHaveBeenLong32Min() throws Exception {
|
||||
setContents("28" + "FFFFFFFF80000000");
|
||||
r.readLong();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipLong() throws Exception {
|
||||
public void testSkipLong64() throws Exception {
|
||||
setContents("28" + "0000000080000000");
|
||||
r.skipLong();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws Exception {
|
||||
setContents("21" + "7F" + "21" + "80"
|
||||
+ "22" + "7FFF" + "22" + "8000"
|
||||
+ "24" + "7FFFFFFF" + "24" + "80000000");
|
||||
assertEquals(Byte.MAX_VALUE, r.readInt());
|
||||
assertEquals(Byte.MIN_VALUE, r.readInt());
|
||||
assertEquals(Short.MAX_VALUE, r.readInt());
|
||||
assertEquals(Short.MIN_VALUE, r.readInt());
|
||||
assertEquals(Integer.MAX_VALUE, r.readInt());
|
||||
assertEquals(Integer.MIN_VALUE, r.readInt());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipInt() throws Exception {
|
||||
setContents("21" + "7F" + "22" + "7FFF" + "24" + "7FFFFFFF");
|
||||
r.skipInt();
|
||||
r.skipInt();
|
||||
r.skipInt();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDouble() throws Exception {
|
||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||
@@ -162,7 +222,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString8() throws Exception {
|
||||
String longest = getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// "foo", the empty string, and 127 random letters
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
@@ -186,7 +246,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testSkipString8() throws Exception {
|
||||
String longest = getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// "foo", the empty string, and 127 random letters
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" +
|
||||
"41" + "7F" + longHex);
|
||||
@@ -199,9 +259,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString16() throws Exception {
|
||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||
String longest = getRandomString(Short.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// 128 random letters and 2^15 -1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
assertEquals(shortest, r.readString());
|
||||
@@ -213,7 +273,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
public void testReadString16ChecksMaxLength() throws Exception {
|
||||
int maxBufferSize = Byte.MAX_VALUE + 1;
|
||||
String valid = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
||||
String validHex = toHexString(valid.getBytes(UTF_8));
|
||||
String invalidhex = validHex + "20";
|
||||
// 128 random letters, the same plus a space
|
||||
setContents("42" + "0080" + validHex
|
||||
@@ -223,12 +283,20 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadString16CouldHaveBeenString8() throws Exception {
|
||||
String longest = getRandomString(Byte.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
setContents("42" + "007F" + longHex);
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString16() throws Exception {
|
||||
String shortest = getRandomString(Byte.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||
String longest = getRandomString(Short.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes("UTF-8"));
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
// 128 random letters and 2^15 - 1 random letters
|
||||
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
|
||||
r.skipString();
|
||||
@@ -239,7 +307,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadString32() throws Exception {
|
||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||
// 2^15 random letters
|
||||
setContents("44" + "00008000" + shortHex);
|
||||
assertEquals(shortest, r.readString());
|
||||
@@ -250,7 +318,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
public void testReadString32ChecksMaxLength() throws Exception {
|
||||
int maxBufferSize = Short.MAX_VALUE + 1;
|
||||
String valid = getRandomString(maxBufferSize);
|
||||
String validHex = toHexString(valid.getBytes("UTF-8"));
|
||||
String validHex = toHexString(valid.getBytes(UTF_8));
|
||||
String invalidHex = validHex + "20";
|
||||
// 2^15 random letters, the same plus a space
|
||||
setContents("44" + "00008000" + validHex +
|
||||
@@ -260,10 +328,18 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadString32CouldHaveBeenString16() throws Exception {
|
||||
String longest = getRandomString(Short.MAX_VALUE);
|
||||
String longHex = toHexString(longest.getBytes(UTF_8));
|
||||
setContents("44" + "00007FFF" + longHex);
|
||||
r.readString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString32() throws Exception {
|
||||
String shortest = getRandomString(Short.MAX_VALUE + 1);
|
||||
String shortHex = toHexString(shortest.getBytes("UTF-8"));
|
||||
String shortHex = toHexString(shortest.getBytes(UTF_8));
|
||||
// 2^15 random letters, twice
|
||||
setContents("44" + "00008000" + shortHex +
|
||||
"44" + "00008000" + shortHex);
|
||||
@@ -275,7 +351,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testReadUtf8String() throws Exception {
|
||||
String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3";
|
||||
String hex = toHexString(unicode.getBytes("UTF-8"));
|
||||
String hex = toHexString(unicode.getBytes(UTF_8));
|
||||
// STRING_8 tag, "foo", the empty string, and the test string
|
||||
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
|
||||
assertEquals("foo", r.readString());
|
||||
@@ -348,6 +424,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadRaw16CouldHaveBeenRaw8() throws Exception {
|
||||
byte[] longest = new byte[Byte.MAX_VALUE];
|
||||
String longHex = toHexString(longest);
|
||||
setContents("52" + "007F" + longHex);
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRaw16() throws Exception {
|
||||
byte[] shortest = new byte[Byte.MAX_VALUE + 1];
|
||||
@@ -385,6 +469,14 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testReadRaw32CouldHaveBeenRaw16() throws Exception {
|
||||
byte[] longest = new byte[Short.MAX_VALUE];
|
||||
String longHex = toHexString(longest);
|
||||
setContents("54" + "00007FFF" + longHex);
|
||||
r.readRaw();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRaw32() throws Exception {
|
||||
byte[] shortest = new byte[Short.MAX_VALUE + 1];
|
||||
@@ -434,25 +526,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListManually() throws Exception {
|
||||
// A list containing 1, "foo", and null
|
||||
setContents("60" + "21" + "01" +
|
||||
"41" + "03" + "666F6F" +
|
||||
"00" + "80");
|
||||
r.readListStart();
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals(1, r.readLong());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
assertTrue(r.hasListEnd());
|
||||
r.readListEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipList() throws Exception {
|
||||
// A list containing 1, "foo", and 128
|
||||
@@ -465,9 +538,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDictionary() throws Exception {
|
||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
||||
setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
|
||||
"41" + "03" + "626172" + "00" + "80");
|
||||
// A dictionary containing "bar" -> null and "foo" -> 123
|
||||
setContents("70" + "41" + "03" + "626172" + "00" +
|
||||
"41" + "03" + "666F6F" + "21" + "7B" + "80");
|
||||
BdfDictionary dictionary = r.readDictionary();
|
||||
assertEquals(2, dictionary.size());
|
||||
assertTrue(dictionary.containsKey("foo"));
|
||||
@@ -517,26 +590,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDictionaryManually() throws Exception {
|
||||
// A dictionary containing "foo" -> 123 and "bar" -> null
|
||||
setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
|
||||
"41" + "03" + "626172" + "00" + "80");
|
||||
r.readDictionaryStart();
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals(123, r.readLong());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertEquals("bar", r.readString());
|
||||
assertFalse(r.hasDictionaryEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
assertTrue(r.hasDictionaryEnd());
|
||||
r.readDictionaryEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipDictionary() throws Exception {
|
||||
// A map containing "foo" -> 123 and "bar" -> null
|
||||
@@ -557,10 +610,10 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testNestedListWithinDepthLimit() throws Exception {
|
||||
// A list containing a list containing a list containing a list...
|
||||
String lists = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "60";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "80";
|
||||
setContents(lists);
|
||||
StringBuilder lists = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("60");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists.append("80");
|
||||
setContents(lists.toString());
|
||||
r.readList();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
@@ -568,23 +621,25 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNestedListOutsideDepthLimit() throws Exception {
|
||||
// A list containing a list containing a list containing a list...
|
||||
String lists = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "60";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "80";
|
||||
setContents(lists);
|
||||
StringBuilder lists = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("60");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists.append("80");
|
||||
setContents(lists.toString());
|
||||
r.readList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedDictionaryWithinDepthLimit() throws Exception {
|
||||
// A dictionary containing a dictionary containing a dictionary...
|
||||
String dicts = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
||||
dicts += "70" + "41" + "03" + "666F6F";
|
||||
dicts += "11";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++)
|
||||
dicts += "80";
|
||||
setContents(dicts);
|
||||
StringBuilder dicts = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||
dicts.append("70").append("41").append("03").append("666F6F");
|
||||
}
|
||||
dicts.append("11");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) {
|
||||
dicts.append("80");
|
||||
}
|
||||
setContents(dicts.toString());
|
||||
r.readDictionary();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
@@ -592,13 +647,15 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNestedDictionaryOutsideDepthLimit() throws Exception {
|
||||
// A dictionary containing a dictionary containing a dictionary...
|
||||
String dicts = "";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
||||
dicts += "70" + "41" + "03" + "666F6F";
|
||||
dicts += "11";
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++)
|
||||
dicts += "80";
|
||||
setContents(dicts);
|
||||
StringBuilder dicts = new StringBuilder();
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||
dicts.append("70").append("41").append("03").append("666F6F");
|
||||
}
|
||||
dicts.append("11");
|
||||
for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) {
|
||||
dicts.append("80");
|
||||
}
|
||||
setContents(dicts.toString());
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
@@ -625,6 +682,6 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
private void setContents(String hex, int maxBufferSize)
|
||||
throws FormatException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
|
||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize);
|
||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
|
||||
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BdfReaderWriterIntegrationTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testConvertStringToCanonicalForm() throws Exception {
|
||||
// 'foo' as a STRING_16 (not canonical, should be a STRING_8)
|
||||
String hexIn = "42" + "0003" + "666F6F";
|
||||
InputStream in = new ByteArrayInputStream(fromHexString(hexIn));
|
||||
BdfReader r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE, false); // Accept non-canonical
|
||||
String s = r.readString();
|
||||
assertEquals("foo", s);
|
||||
assertTrue(r.eof());
|
||||
// Convert the string back to BDF
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
BdfWriter w = new BdfWriterImpl(out);
|
||||
w.writeString(s);
|
||||
w.flush();
|
||||
String hexOut = toHexString(out.toByteArray());
|
||||
// The BDF should now be in canonical form
|
||||
assertEquals("41" + "03" + "666F6F", hexOut);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertDictionaryToCanonicalForm() throws Exception {
|
||||
// A dictionary with keys in non-canonical order: 'foo' then 'bar'
|
||||
String hexIn = "70" + "41" + "03" + "666F6F" + "21" + "01"
|
||||
+ "41" + "03" + "626172" + "21" + "02" + "80";
|
||||
InputStream in = new ByteArrayInputStream(fromHexString(hexIn));
|
||||
BdfReader r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE, false); // Accept non-canonical
|
||||
BdfDictionary d = r.readDictionary();
|
||||
assertEquals(2, d.size());
|
||||
assertTrue(r.eof());
|
||||
// The entries should be returned in canonical order
|
||||
Iterator<Entry<String, Object>> it = d.entrySet().iterator();
|
||||
Entry<String, Object> first = it.next();
|
||||
assertEquals("bar", first.getKey());
|
||||
assertEquals(2L, first.getValue());
|
||||
Entry<String, Object> second = it.next();
|
||||
assertEquals("foo", second.getKey());
|
||||
assertEquals(1L, second.getValue());
|
||||
|
||||
// Convert a non-canonical map to BDF (use LinkedHashMap so we know
|
||||
// the entries will be iterated over in non-canonical order)
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("foo", 1);
|
||||
m.put("bar", 2);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
BdfWriter w = new BdfWriterImpl(out);
|
||||
w.writeDictionary(m);
|
||||
w.flush();
|
||||
String hexOut = toHexString(out.toByteArray());
|
||||
// The entries should be in canonical order: 'bar' then 'foo'
|
||||
assertEquals("70" + "41" + "03" + "626172" + "21" + "02"
|
||||
+ "41" + "03" + "666F6F" + "21" + "01" + "80", hexOut);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
@@ -168,9 +169,11 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testWriteDictionary() throws IOException {
|
||||
// Use LinkedHashMap to get predictable iteration order
|
||||
// Add entries to dictionary in descending order - they should be
|
||||
// output in ascending order. Use LinkedHashMap to get predictable
|
||||
// iteration order
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
for (int i = 0; i < 4; i++) m.put(String.valueOf(i), i);
|
||||
for (int i = 3; i >= 0; i--) m.put(String.valueOf(i), i);
|
||||
w.writeDictionary(m);
|
||||
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||
@@ -180,30 +183,17 @@ public class BdfWriterImplTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDelimitedList() throws IOException {
|
||||
w.writeListStart();
|
||||
w.writeLong(1);
|
||||
w.writeString("foo");
|
||||
w.writeLong(128);
|
||||
w.writeListEnd();
|
||||
// LIST tag, 1 as integer, "foo" as string, 128 as integer, END tag
|
||||
checkContents("60" + "21" + "01" +
|
||||
"41" + "03" + "666F6F" +
|
||||
"22" + "0080" + "80");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDelimitedDictionary() throws IOException {
|
||||
w.writeDictionaryStart();
|
||||
w.writeString("foo");
|
||||
w.writeLong(123);
|
||||
w.writeString("bar");
|
||||
w.writeNull();
|
||||
w.writeDictionaryEnd();
|
||||
// DICTIONARY tag, "foo" as string, 123 as integer, "bar" as string,
|
||||
// NULL tag, END tag
|
||||
checkContents("70" + "41" + "03" + "666F6F" +
|
||||
"21" + "7B" + "41" + "03" + "626172" + "00" + "80");
|
||||
public void testWriteBdfDictionary() throws IOException {
|
||||
// Add entries to dictionary in descending order - they should be
|
||||
// output in ascending order
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
for (int i = 3; i >= 0; i--) d.put(String.valueOf(i), i);
|
||||
w.writeDictionary(d);
|
||||
// DICTIONARY tag, keys as strings and values as integers, END tag
|
||||
checkContents("70" + "41" + "01" + "30" + "21" + "00" +
|
||||
"41" + "01" + "31" + "21" + "01" +
|
||||
"41" + "01" + "32" + "21" + "02" +
|
||||
"41" + "01" + "33" + "21" + "03" + "80");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -2,21 +2,27 @@ package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -26,32 +32,29 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory =
|
||||
context.mock(BdfReaderFactory.class);
|
||||
private final QrCodeClassifier qrCodeClassifier =
|
||||
context.mock(QrCodeClassifier.class);
|
||||
private final BdfReader bdfReader = context.mock(BdfReader.class);
|
||||
|
||||
private final PayloadParserImpl payloadParser =
|
||||
new PayloadParserImpl(bdfReaderFactory);
|
||||
private final String payload = getRandomString(123);
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testThrowsFormatExceptionIfPayloadIsEmpty() throws Exception {
|
||||
payloadParser.parse(new byte[0]);
|
||||
private final PayloadParserImpl payloadParser =
|
||||
new PayloadParserImpl(bdfReaderFactory, qrCodeClassifier);
|
||||
|
||||
@Test(expected = WrongQrCodeTypeException.class)
|
||||
public void testThrowsExceptionForWrongQrCodeType() throws Exception {
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForOldVersion()
|
||||
throws Exception {
|
||||
try {
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION - 1});
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertTrue(e.isTooOld());
|
||||
}
|
||||
}
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION - 1);
|
||||
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForBetaVersion()
|
||||
throws Exception {
|
||||
try {
|
||||
payloadParser.parse(new byte[] {BETA_PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertTrue(e.isTooOld());
|
||||
@@ -61,8 +64,10 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testThrowsUnsupportedVersionExceptionForNewVersion()
|
||||
throws Exception {
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION + 1);
|
||||
|
||||
try {
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION + 1});
|
||||
payloadParser.parse(payload);
|
||||
fail();
|
||||
} catch (UnsupportedVersionException e) {
|
||||
assertFalse(e.isTooOld());
|
||||
@@ -71,6 +76,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testThrowsFormatExceptionForEmptyList() throws Exception {
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -79,7 +86,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(new BdfList()));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -87,6 +94,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -97,7 +106,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(false));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -105,6 +114,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -115,7 +125,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
@@ -123,6 +133,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -133,12 +144,14 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
payloadParser.parse(payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsPayloadWithNoDescriptors() throws Exception {
|
||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(bdfReaderFactory).createReader(
|
||||
with(any(ByteArrayInputStream.class)));
|
||||
@@ -149,8 +162,16 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
Payload p = payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
||||
Payload p = payloadParser.parse(payload);
|
||||
assertArrayEquals(commitment, p.getCommitment());
|
||||
assertTrue(p.getTransportDescriptors().isEmpty());
|
||||
}
|
||||
|
||||
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||
int formatVersion) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
@@ -9,17 +10,23 @@ import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEven
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.hasEvent;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class MailboxManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -125,4 +132,32 @@ public class MailboxManagerImplTest extends BrambleMockTestCase {
|
||||
assertTrue(manager.checkConnection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertBase32Payload() throws FormatException {
|
||||
byte[] payload = getRandomBytes(65);
|
||||
String base32payload = Base32.encode(payload).toLowerCase(Locale.ROOT);
|
||||
String expected = new String(payload, ISO_8859_1);
|
||||
try {
|
||||
manager.convertBase32Payload("foo bar");
|
||||
fail();
|
||||
} catch (FormatException e) {
|
||||
// expected
|
||||
}
|
||||
try { // doesn't work with shorter link
|
||||
manager.convertBase32Payload("briar-mailbox://" +
|
||||
base32payload.substring(0, base32payload.length() - 1));
|
||||
fail();
|
||||
} catch (FormatException e) {
|
||||
// expected
|
||||
}
|
||||
// works with white-spaces
|
||||
assertEquals(expected, manager.convertBase32Payload(
|
||||
"foo bar briar-mailbox://" + base32payload + " foo bar"));
|
||||
// even works without white-space at the end
|
||||
assertEquals(expected, manager.convertBase32Payload(
|
||||
"foo bar briar-mailbox://" + base32payload + "foobar"));
|
||||
// even works without schema and extra chars at end
|
||||
assertEquals(expected, manager.convertBase32Payload(
|
||||
"foo bar " + base32payload + "foobar"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -7,13 +8,24 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
@@ -27,6 +39,9 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
@@ -48,9 +63,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
context.mock(MailboxSettingsManager.class);
|
||||
private final MailboxUpdateManager mailboxUpdateManager =
|
||||
context.mock(MailboxUpdateManager.class);
|
||||
private final MailboxPairingTaskFactory factory =
|
||||
new MailboxPairingTaskFactoryImpl(executor, db, crypto, clock, api,
|
||||
mailboxSettingsManager, mailboxUpdateManager);
|
||||
private final QrCodeClassifier qrCodeClassifier =
|
||||
context.mock(QrCodeClassifier.class);
|
||||
|
||||
private final String onion = getRandomString(56);
|
||||
private final byte[] onionBytes = getRandomBytes(32);
|
||||
@@ -68,32 +82,50 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testInitialQrCodeReceivedState() {
|
||||
MailboxPairingTask task =
|
||||
factory.createPairingTask(getRandomString(42));
|
||||
MailboxPairingTask task = createPairingTask(getRandomString(42));
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.QrCodeReceived)
|
||||
);
|
||||
assertTrue(state instanceof QrCodeReceived));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCode() {
|
||||
MailboxPairingTask task1 =
|
||||
factory.createPairingTask(getRandomString(42));
|
||||
task1.run();
|
||||
task1.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
||||
);
|
||||
public void testInvalidQrCodeType() {
|
||||
String payload = getRandomString(65);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
String goodLength = "00" + getRandomString(63);
|
||||
MailboxPairingTask task2 = factory.createPairingTask(goodLength);
|
||||
task2.run();
|
||||
task2.addObserver(state ->
|
||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
||||
);
|
||||
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCodeVersion() {
|
||||
String payload = getRandomString(65);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION + 1);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidQrCodeLength() {
|
||||
String payload = getRandomString(42);
|
||||
MailboxPairingTask task = createPairingTask(payload);
|
||||
|
||||
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||
|
||||
task.run();
|
||||
task.addObserver(state ->
|
||||
assertTrue(state instanceof InvalidQrCode));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulPairing() throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -121,17 +153,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
AtomicInteger i = new AtomicInteger(0);
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.addObserver(state -> {
|
||||
if (i.get() == 0) {
|
||||
assertEquals(MailboxPairingState.QrCodeReceived.class,
|
||||
state.getClass());
|
||||
assertEquals(QrCodeReceived.class, state.getClass());
|
||||
} else if (i.get() == 1) {
|
||||
assertEquals(MailboxPairingState.Pairing.class,
|
||||
state.getClass());
|
||||
assertEquals(Pairing.class, state.getClass());
|
||||
} else if (i.get() == 2) {
|
||||
assertEquals(MailboxPairingState.Paired.class,
|
||||
state.getClass());
|
||||
assertEquals(Paired.class, state.getClass());
|
||||
} else fail("Unexpected change of state " + state.getClass());
|
||||
i.getAndIncrement();
|
||||
});
|
||||
@@ -140,24 +169,23 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testAlreadyPaired() throws Exception {
|
||||
testApiException(new MailboxApi.MailboxAlreadyPairedException(),
|
||||
MailboxPairingState.MailboxAlreadyPaired.class);
|
||||
testApiException(new MailboxAlreadyPairedException(),
|
||||
MailboxAlreadyPaired.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMailboxApiException() throws Exception {
|
||||
testApiException(new MailboxApi.ApiException(),
|
||||
MailboxPairingState.UnexpectedError.class);
|
||||
testApiException(new ApiException(), UnexpectedError.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApiIOException() throws Exception {
|
||||
testApiException(new IOException(),
|
||||
MailboxPairingState.ConnectionError.class);
|
||||
testApiException(new IOException(), ConnectionError.class);
|
||||
}
|
||||
|
||||
private void testApiException(Exception e,
|
||||
Class<? extends MailboxPairingState> s) throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -165,13 +193,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
will(throwException(e));
|
||||
}});
|
||||
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.run();
|
||||
task.addObserver(state -> assertEquals(state.getClass(), s));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbException() throws Exception {
|
||||
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).encodeOnion(onionBytes);
|
||||
will(returnValue(onion));
|
||||
@@ -188,10 +217,10 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
will(throwException(new DbException()));
|
||||
}});
|
||||
|
||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
||||
MailboxPairingTask task = createPairingTask(validPayload);
|
||||
task.run();
|
||||
task.addObserver(state -> assertEquals(state.getClass(),
|
||||
MailboxPairingState.UnexpectedError.class));
|
||||
task.addObserver(state ->
|
||||
assertEquals(state.getClass(), UnexpectedError.class));
|
||||
}
|
||||
|
||||
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
|
||||
@@ -202,4 +231,22 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
p1.getServerSupports().equals(p2.getServerSupports()));
|
||||
}
|
||||
|
||||
private MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(time));
|
||||
}});
|
||||
|
||||
return new MailboxPairingTaskImpl(qrCodePayload, executor, db,
|
||||
crypto, clock, api, mailboxSettingsManager,
|
||||
mailboxUpdateManager, qrCodeClassifier);
|
||||
}
|
||||
|
||||
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||
int formatVersion) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.mailbox;
|
||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.SocketFactory;
|
||||
@@ -11,20 +10,24 @@ import javax.net.SocketFactory;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_ID;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
class MailboxTestUtils {
|
||||
|
||||
static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) {
|
||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||
byte[] payloadBytes = ByteBuffer.allocate(65)
|
||||
.put((byte) 32) // 1
|
||||
.put((byte) formatIdAndVersion) // 1
|
||||
.put(onionBytes) // 32
|
||||
.put(setupToken) // 32
|
||||
.array();
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new String(payloadBytes, Charset.forName("ISO-8859-1"));
|
||||
return new String(payloadBytes, ISO_8859_1);
|
||||
}
|
||||
|
||||
// Used by mailbox integration tests
|
||||
static String getQrCodePayload(byte[] setupToken) {
|
||||
return getQrCodePayload(getRandomId(), setupToken);
|
||||
}
|
||||
|
||||
@@ -217,13 +217,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
||||
// The plugin should have bound a socket and stored the port number
|
||||
BdfList descriptor = kal.getDescriptor();
|
||||
assertEquals(3, descriptor.size());
|
||||
assertEquals(TRANSPORT_ID_LAN, descriptor.getLong(0).longValue());
|
||||
assertEquals(TRANSPORT_ID_LAN, descriptor.getInt(0).intValue());
|
||||
byte[] address = descriptor.getRaw(1);
|
||||
InetAddress addr = InetAddress.getByAddress(address);
|
||||
assertTrue(addr instanceof Inet4Address);
|
||||
assertFalse(addr.isLoopbackAddress());
|
||||
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
|
||||
int port = descriptor.getLong(2).intValue();
|
||||
int port = descriptor.getInt(2);
|
||||
assertTrue(port > 0 && port < 65536);
|
||||
// The plugin should be listening on the port
|
||||
InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
|
||||
|
||||
@@ -404,7 +404,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -471,7 +471,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup2.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -526,7 +526,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||
will(returnValue(update));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -564,7 +564,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, updateId, false);
|
||||
will(returnValue(update));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
@@ -695,7 +695,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(localGroupMessageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||
false);
|
||||
will(returnValue(oldUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
oldPropertiesDict);
|
||||
@@ -760,7 +761,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(localGroupMessageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId);
|
||||
oneOf(clientHelper).getMessageAsList(txn, localGroupUpdateId,
|
||||
false);
|
||||
will(returnValue(oldUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
oldPropertiesDict);
|
||||
@@ -819,12 +821,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
localGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
// Retrieve and parse the latest local properties
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999);
|
||||
oneOf(clientHelper).getMessageAsList(txn, fooVersion999, false);
|
||||
will(returnValue(fooUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
fooPropertiesDict);
|
||||
will(returnValue(fooProperties));
|
||||
oneOf(clientHelper).getMessageAsList(txn, barVersion3);
|
||||
oneOf(clientHelper).getMessageAsList(txn, barVersion3, false);
|
||||
will(returnValue(barUpdate));
|
||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||
barPropertiesDict);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class QrCodeClassifierImplTest extends BrambleTestCase {
|
||||
|
||||
private final QrCodeClassifier classifier = new QrCodeClassifierImpl();
|
||||
|
||||
@Test
|
||||
public void testClassifiesEmptyStringAsUnknown() {
|
||||
Pair<QrCodeType, Integer> result = classifier.classifyQrCode("");
|
||||
assertEquals(UNKNOWN, result.getFirst());
|
||||
assertEquals(0, result.getSecond().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesKeyAgreement() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
for (int version = 0; version < 32; version++) {
|
||||
int typeAndVersion =
|
||||
(KeyAgreementConstants.QR_FORMAT_ID << 5) | version;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result =
|
||||
classifier.classifyQrCode(payload);
|
||||
assertEquals(BQP, result.getFirst());
|
||||
assertEquals(version, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesMailbox() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
for (int version = 0; version < 32; version++) {
|
||||
int typeAndVersion =
|
||||
(MailboxConstants.QR_FORMAT_ID << 5) | version;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result =
|
||||
classifier.classifyQrCode(payload);
|
||||
assertEquals(MAILBOX, result.getFirst());
|
||||
assertEquals(version, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifiesUnknownFormatIdAsUnknown() {
|
||||
byte[] payloadBytes = getRandomBytes(123);
|
||||
int unknownFormatId = MailboxConstants.QR_FORMAT_ID + 1;
|
||||
int typeAndVersion = unknownFormatId << 5;
|
||||
payloadBytes[0] = (byte) typeAndVersion;
|
||||
String payload = new String(payloadBytes, ISO_8859_1);
|
||||
Pair<QrCodeType, Integer> result = classifier.classifyQrCode(payload);
|
||||
assertEquals(UNKNOWN, result.getFirst());
|
||||
assertEquals(0, result.getSecond().intValue());
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,18 @@ import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.Offer;
|
||||
import org.briarproject.bramble.api.sync.Priority;
|
||||
import org.briarproject.bramble.api.sync.Request;
|
||||
import org.briarproject.bramble.api.sync.SyncRecordReader;
|
||||
import org.briarproject.bramble.api.sync.Versions;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.PredicateMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -23,12 +28,15 @@ import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
@@ -46,6 +54,38 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
private final SyncRecordReader reader =
|
||||
new SyncRecordReaderImpl(messageFactory, recordReader);
|
||||
|
||||
@Test
|
||||
public void testNoFormatExceptionIfMessageIsMinimumSize() throws Exception {
|
||||
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH + 1));
|
||||
expectCreateMessage(1);
|
||||
|
||||
reader.readMessage();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFormatExceptionIfMessageIsTooSmall() throws Exception {
|
||||
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH));
|
||||
|
||||
reader.readMessage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoFormatExceptionIfMessageIsMaximumSize() throws Exception {
|
||||
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH
|
||||
+ MAX_MESSAGE_BODY_LENGTH));
|
||||
expectCreateMessage(MAX_MESSAGE_BODY_LENGTH);
|
||||
|
||||
reader.readMessage();
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFormatExceptionIfMessageIsTooLarge() throws Exception {
|
||||
expectReadRecord(createMessage(MESSAGE_HEADER_LENGTH
|
||||
+ MAX_MESSAGE_BODY_LENGTH + 1));
|
||||
|
||||
reader.readMessage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
||||
expectReadRecord(createAck());
|
||||
@@ -158,6 +198,20 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
assertTrue(reader.eof());
|
||||
}
|
||||
|
||||
private void expectCreateMessage(int bodyLength) {
|
||||
MessageId messageId = new MessageId(getRandomId());
|
||||
GroupId groupId = new GroupId(getRandomId());
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
Matcher<byte[]> matcher = new PredicateMatcher<>(byte[].class,
|
||||
b -> b.length == MESSAGE_HEADER_LENGTH + bodyLength);
|
||||
oneOf(messageFactory).createMessage(with(matcher));
|
||||
will(returnValue(new Message(messageId, groupId, timestamp,
|
||||
new byte[bodyLength])));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
//noinspection unchecked
|
||||
@@ -167,6 +221,10 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
}
|
||||
|
||||
private Record createMessage(int payloadLength) {
|
||||
return new Record(PROTOCOL_VERSION, MESSAGE, new byte[payloadLength]);
|
||||
}
|
||||
|
||||
private Record createAck() throws Exception {
|
||||
return new Record(PROTOCOL_VERSION, ACK, createPayload());
|
||||
}
|
||||
|
||||
@@ -88,51 +88,58 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8() {
|
||||
public void testFromUtf8AcceptsNullCharacterUsingStandardUtf8()
|
||||
throws Exception {
|
||||
// The UTF-8 encoding of the null character is valid
|
||||
assertEquals("\u0000", StringUtils.fromUtf8(new byte[1]));
|
||||
byte[] utf8 = new byte[1];
|
||||
String actual = StringUtils.fromUtf8(utf8);
|
||||
assertEquals("\u0000", actual);
|
||||
// When we convert back to UTF-8 we should get the original encoding
|
||||
assertArrayEquals(utf8, StringUtils.toUtf8(actual));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8RemovesNullCharacterUsingModifiedUtf8() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromUtf8RejectsNullCharacterUsingModifiedUtf8()
|
||||
throws Exception {
|
||||
// The modified UTF-8 encoding of the null character is not valid
|
||||
byte[] b = new byte[] {
|
||||
(byte) 0xC0, (byte) 0x80, // Null character as modified UTF-8
|
||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
// Conversion should ignore the invalid character and return the rest
|
||||
String expected = "\u0205";
|
||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
||||
StringUtils.fromUtf8(b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8() {
|
||||
public void testFromUtf8AcceptsSupplementaryCharacterUsingStandardUtf8()
|
||||
throws Exception {
|
||||
// The UTF-8 encoding of a supplementary character is valid and should
|
||||
// be converted to a surrogate pair
|
||||
byte[] b = new byte[] {
|
||||
byte[] utf8 = new byte[] {
|
||||
(byte) 0xF0, (byte) 0x90, (byte) 0x90, (byte) 0x80, // U+10400
|
||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
String expected = "\uD801\uDC00\u0205"; // Surrogate pair
|
||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
||||
String actual = StringUtils.fromUtf8(utf8);
|
||||
assertEquals(expected, actual);
|
||||
// When we convert back to UTF-8 we should get the original encoding
|
||||
assertArrayEquals(utf8, StringUtils.toUtf8(actual));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8RemovesSupplementaryCharacterUsingModifiedUtf8() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromUtf8RejectsSupplementaryCharacterUsingModifiedUtf8()
|
||||
throws Exception {
|
||||
// The CESU-8 or modified UTF-8 encoding of a supplementary character
|
||||
// is not valid
|
||||
byte[] b = new byte[] {
|
||||
byte[] utf8 = new byte[] {
|
||||
(byte) 0xED, (byte) 0xA0, (byte) 0x81, // U+10400 as CSEU-8
|
||||
(byte) 0xED, (byte) 0xB0, (byte) 0x80,
|
||||
(byte) 0xC8, (byte) 0x85 // U+0205
|
||||
};
|
||||
// Conversion should ignore the invalid character and return the rest
|
||||
String expected = "\u0205";
|
||||
assertEquals(expected, StringUtils.fromUtf8(b));
|
||||
StringUtils.fromUtf8(utf8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromUtf8EmptyInput() {
|
||||
public void testFromUtf8EmptyInput() throws Exception {
|
||||
assertEquals("", StringUtils.fromUtf8(new byte[0]));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Semaphore;
|
||||
@@ -33,6 +32,7 @@ import static java.util.logging.Level.WARNING;
|
||||
import static jssc.SerialPort.PURGE_RXCLEAR;
|
||||
import static jssc.SerialPort.PURGE_TXCLEAR;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -41,7 +41,6 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ModemImpl.class.getName());
|
||||
|
||||
private static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
private static final int MAX_LINE_LENGTH = 256;
|
||||
private static final int[] BAUD_RATES = {
|
||||
256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200
|
||||
|
||||
@@ -30,8 +30,8 @@ dependencyVerification {
|
||||
'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b',
|
||||
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
|
||||
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
|
||||
'org.briarproject:tor-linux:0.4.5.14:tor-linux-0.4.5.14.jar:1844e54cf6df0c85cec219381a3364c759ae444a6b63f7558b757becb7d41d08',
|
||||
'org.briarproject:tor-windows:0.4.5.14:tor-windows-0.4.5.14.jar:d337afa1043f0cfa7e6e8c2473d682a5663a2c8052bb97a770450893c78c9b4f',
|
||||
'org.briarproject:tor-linux:0.4.7.13:tor-linux-0.4.7.13.jar:9819ee973cbcdc133f7d04aef9d4b957a35087627a790e532142d15412a9636f',
|
||||
'org.briarproject:tor-windows:0.4.7.13:tor-windows-0.4.7.13.jar:853d2769665614e26703cbe02e43b218b064c04a0bcd120fdc459cda45bd2606',
|
||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
|
||||
@@ -26,8 +26,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 31
|
||||
versionCode 10418
|
||||
versionName "1.4.18"
|
||||
versionCode 10421
|
||||
versionName "1.4.21"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.isUiThread;
|
||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_OLD_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
|
||||
@@ -141,11 +140,6 @@ public class BriarService extends Service {
|
||||
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
ongoingChannel.setShowBadge(false);
|
||||
nm.createNotificationChannel(ongoingChannel);
|
||||
// Delete the unused channel previously used for startup
|
||||
// failure notifications
|
||||
// TODO: Remove this ID after a reasonable upgrade period
|
||||
// (added 2021-07-12)
|
||||
nm.deleteNotificationChannel(FAILURE_CHANNEL_ID);
|
||||
}
|
||||
Notification foregroundNotification =
|
||||
notificationManager.getForegroundNotification();
|
||||
|
||||
@@ -41,7 +41,6 @@ import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
|
||||
|
||||
@NotNullByDefault
|
||||
abstract class BaseViewModel extends DbViewModel implements EventListener {
|
||||
@@ -115,7 +114,7 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
|
||||
@DatabaseExecutor
|
||||
private String getPostText(Transaction txn, MessageId m)
|
||||
throws DbException {
|
||||
return HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE);
|
||||
return HtmlUtils.cleanArticle(blogManager.getPostText(txn, m));
|
||||
}
|
||||
|
||||
LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.blog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.Spanned;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -170,7 +171,12 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
// TODO make author clickable #624
|
||||
|
||||
text.setText(c.getComment());
|
||||
if (fullText) text.setTextIsSelectable(true);
|
||||
Linkify.addLinks(text, Linkify.WEB_URLS);
|
||||
text.setMovementMethod(null);
|
||||
if (fullText) {
|
||||
text.setTextIsSelectable(true);
|
||||
makeLinksClickable(text, listener::onLinkClick);
|
||||
}
|
||||
|
||||
commentContainer.addView(v);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.FileImportError;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.FileImportSuccess;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.UrlImportError;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.UrlImportSuccess;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.fragment.ErrorFragment;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
|
||||
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -50,24 +54,29 @@ public class RssFeedActivity extends BriarActivity
|
||||
viewModel.getImportResult().observeEvent(this, this::onImportResult);
|
||||
}
|
||||
|
||||
private void onImportResult(RssFeedViewModel.ImportResult result) {
|
||||
if (result == IMPORTED) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
private void onImportResult(@Nullable RssImportResult result) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (result instanceof UrlImportSuccess) {
|
||||
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
|
||||
onBackPressed();
|
||||
}
|
||||
} else if (result == FAILED) {
|
||||
String url = viewModel.getUrlFailedImport();
|
||||
if (url == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
} else if (result instanceof UrlImportError) {
|
||||
String url = ((UrlImportError) result).url;
|
||||
RssFeedImportFailedDialogFragment dialog =
|
||||
RssFeedImportFailedDialogFragment.newInstance(url);
|
||||
dialog.show(getSupportFragmentManager(),
|
||||
RssFeedImportFailedDialogFragment.TAG);
|
||||
} else if (result == EXISTS) {
|
||||
Toast.makeText(this, R.string.blogs_rss_feeds_import_exists,
|
||||
Toast.LENGTH_LONG).show();
|
||||
dialog.show(fm, RssFeedImportFailedDialogFragment.TAG);
|
||||
} else if (result instanceof FileImportSuccess) {
|
||||
// pop stack back to before the initial import fragment
|
||||
fm.popBackStackImmediate(RssFeedImportFragment.TAG,
|
||||
POP_BACK_STACK_INCLUSIVE);
|
||||
} else if (result instanceof FileImportError) {
|
||||
// pop stack back to initial import fragment
|
||||
fm.popBackStackImmediate(RssFeedImportFragment.TAG, 0);
|
||||
// show error fragment
|
||||
Fragment f = ErrorFragment.newInstance(
|
||||
getString(R.string.blogs_rss_feeds_import_error));
|
||||
String tag = ErrorFragment.TAG;
|
||||
showFragment(fm, f, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
super(new DiffUtil.ItemCallback<Feed>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(Feed a, Feed b) {
|
||||
return a.getUrl().equals(b.getUrl()) &&
|
||||
a.getBlogId().equals(b.getBlogId()) &&
|
||||
return a.getBlogId().equals(b.getBlogId()) &&
|
||||
a.getAdded() == b.getAdded();
|
||||
}
|
||||
|
||||
@@ -86,8 +85,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
delete.setOnClickListener(v -> listener.onDeleteClick(item));
|
||||
|
||||
// Author
|
||||
if (item.getRssAuthor() != null) {
|
||||
author.setText(item.getRssAuthor());
|
||||
if (item.getProperties().getAuthor() != null) {
|
||||
author.setText(item.getProperties().getAuthor());
|
||||
author.setVisibility(VISIBLE);
|
||||
authorLabel.setVisibility(VISIBLE);
|
||||
} else {
|
||||
@@ -100,8 +99,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
updated.setText(formatDate(ctx, item.getUpdated()));
|
||||
|
||||
// Description
|
||||
if (item.getDescription() != null) {
|
||||
description.setText(item.getDescription());
|
||||
if (item.getProperties().getDescription() != null) {
|
||||
description.setText(item.getProperties().getDescription());
|
||||
description.setVisibility(VISIBLE);
|
||||
} else {
|
||||
description.setVisibility(GONE);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@@ -13,18 +17,27 @@ import android.widget.ProgressBar;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.fragment.ProgressFragment;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetContentAdvanced;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.OpenDocumentAdvanced;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
|
||||
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -39,6 +52,15 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
private Button importButton;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@RequiresApi(19)
|
||||
private final ActivityResultLauncher<String[]> docLauncher =
|
||||
registerForActivityResult(new OpenDocumentAdvanced(),
|
||||
this::onFileChosen);
|
||||
|
||||
private final ActivityResultLauncher<String> contentLauncher =
|
||||
registerForActivityResult(new GetContentAdvanced(),
|
||||
this::onFileChosen);
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
@@ -52,6 +74,7 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
||||
if (SDK_INT >= 19) setHasOptionsMenu(true);
|
||||
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
||||
container, false);
|
||||
|
||||
@@ -92,11 +115,40 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (SDK_INT >= 19) {
|
||||
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_import_file && SDK_INT >= 19) {
|
||||
launchActivityToOpenFile(requireContext(), docLauncher,
|
||||
contentLauncher, "*/*");
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void onFileChosen(@Nullable Uri uri) {
|
||||
if (uri == null) return;
|
||||
// show progress fragment
|
||||
Fragment f = ProgressFragment.newInstance(
|
||||
getString(R.string.blogs_rss_feeds_import_progress));
|
||||
String tag = ProgressFragment.TAG;
|
||||
showFragment(getParentFragmentManager(), f, tag);
|
||||
// view model will import and change state that activity will react to
|
||||
viewModel.importFeed(uri);
|
||||
}
|
||||
|
||||
private void enableOrDisableImportButton() {
|
||||
String url = urlInput.getText().toString();
|
||||
importButton.setEnabled(viewModel.validateAndNormaliseUrl(url) != null);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.util.Patterns;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
@@ -11,6 +13,10 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.FileImportError;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.FileImportSuccess;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.UrlImportError;
|
||||
import org.briarproject.briar.android.blog.RssImportResult.UrlImportSuccess;
|
||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||
@@ -20,9 +26,10 @@ import org.briarproject.briar.api.feed.FeedManager;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
@@ -30,6 +37,7 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -38,13 +46,9 @@ import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
|
||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
|
||||
|
||||
@NotNullByDefault
|
||||
class RssFeedViewModel extends DbViewModel {
|
||||
enum ImportResult {IMPORTED, FAILED, EXISTS}
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(RssFeedViewModel.class.getName());
|
||||
@@ -56,11 +60,9 @@ class RssFeedViewModel extends DbViewModel {
|
||||
private final MutableLiveData<LiveResult<List<Feed>>> feeds =
|
||||
new MutableLiveData<>();
|
||||
|
||||
@Nullable
|
||||
private volatile String urlFailedImport = null;
|
||||
private final MutableLiveData<Boolean> isImporting =
|
||||
new MutableLiveData<>(false);
|
||||
private final MutableLiveEvent<ImportResult> importResult =
|
||||
private final MutableLiveEvent<RssImportResult> importResult =
|
||||
new MutableLiveEvent<>();
|
||||
|
||||
@Inject
|
||||
@@ -101,7 +103,6 @@ class RssFeedViewModel extends DbViewModel {
|
||||
private List<Feed> loadFeeds(Transaction txn) throws DbException {
|
||||
long start = now();
|
||||
List<Feed> feeds = feedManager.getFeeds(txn);
|
||||
Collections.sort(feeds);
|
||||
logDuration(LOG, "Loading feeds", start);
|
||||
return feeds;
|
||||
}
|
||||
@@ -125,7 +126,7 @@ class RssFeedViewModel extends DbViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
LiveEvent<ImportResult> getImportResult() {
|
||||
LiveEvent<RssImportResult> getImportResult() {
|
||||
return importResult;
|
||||
}
|
||||
|
||||
@@ -135,44 +136,52 @@ class RssFeedViewModel extends DbViewModel {
|
||||
|
||||
void importFeed(String url) {
|
||||
isImporting.setValue(true);
|
||||
urlFailedImport = null;
|
||||
ioExecutor.execute(() -> {
|
||||
try {
|
||||
if (exists(url)) {
|
||||
importResult.postEvent(EXISTS);
|
||||
return;
|
||||
}
|
||||
Feed feed = feedManager.addFeed(url);
|
||||
List<Feed> updated = addListItem(getList(feeds), feed);
|
||||
if (updated != null) {
|
||||
Collections.sort(updated);
|
||||
feeds.postValue(new LiveResult<>(updated));
|
||||
// Update the feed if it was already present
|
||||
List<Feed> feedList = getList(feeds);
|
||||
if (feedList == null) feedList = new ArrayList<>();
|
||||
List<Feed> updated = updateListItems(feedList,
|
||||
f -> f.equals(feed), f -> feed);
|
||||
// Add the feed if it wasn't already present
|
||||
if (updated == null) {
|
||||
feedList.add(feed);
|
||||
updated = feedList;
|
||||
}
|
||||
importResult.postEvent(IMPORTED);
|
||||
feeds.postValue(new LiveResult<>(updated));
|
||||
importResult.postEvent(new UrlImportSuccess());
|
||||
} catch (DbException | IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
urlFailedImport = url;
|
||||
importResult.postEvent(FAILED);
|
||||
importResult.postEvent(new UrlImportError(url));
|
||||
} finally {
|
||||
isImporting.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getUrlFailedImport() {
|
||||
return urlFailedImport;
|
||||
}
|
||||
|
||||
private boolean exists(String url) {
|
||||
List<Feed> list = getList(feeds);
|
||||
if (list != null) {
|
||||
for (Feed feed : list) {
|
||||
if (url.equals(feed.getUrl())) {
|
||||
return true;
|
||||
@UiThread
|
||||
void importFeed(Uri uri) {
|
||||
ContentResolver contentResolver = getApplication().getContentResolver();
|
||||
ioExecutor.execute(() -> {
|
||||
try (InputStream is = contentResolver.openInputStream(uri)) {
|
||||
Feed feed = feedManager.addFeed(is);
|
||||
// Update the feed if it was already present
|
||||
List<Feed> feedList = getList(feeds);
|
||||
if (feedList == null) feedList = new ArrayList<>();
|
||||
List<Feed> updated = updateListItems(feedList,
|
||||
f -> f.equals(feed), f -> feed);
|
||||
// Add the feed if it wasn't already present
|
||||
if (updated == null) {
|
||||
feedList.add(feed);
|
||||
updated = feedList;
|
||||
}
|
||||
feeds.postValue(new LiveResult<>(updated));
|
||||
importResult.postEvent(new FileImportSuccess());
|
||||
} catch (IOException | DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
importResult.postEvent(new FileImportError());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
abstract class RssImportResult {
|
||||
|
||||
static class UrlImportSuccess extends RssImportResult {
|
||||
}
|
||||
|
||||
static class UrlImportError extends RssImportResult {
|
||||
final String url;
|
||||
|
||||
UrlImportError(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
static class FileImportSuccess extends RssImportResult {
|
||||
}
|
||||
|
||||
static class FileImportError extends RssImportResult {
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,14 @@ package org.briarproject.briar.android.contact.add.nearby;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
abstract class AddContactState {
|
||||
|
||||
static class KeyAgreementListening extends AddContactState {
|
||||
|
||||
final Bitmap qrCode;
|
||||
|
||||
KeyAgreementListening(Bitmap qrCode) {
|
||||
@@ -29,6 +31,7 @@ abstract class AddContactState {
|
||||
}
|
||||
|
||||
static class ContactExchangeFinished extends AddContactState {
|
||||
|
||||
final ContactExchangeResult result;
|
||||
|
||||
ContactExchangeFinished(ContactExchangeResult result) {
|
||||
@@ -37,25 +40,34 @@ abstract class AddContactState {
|
||||
}
|
||||
|
||||
static class Failed extends AddContactState {
|
||||
/**
|
||||
* Non-null if failed due to the scanned QR code version.
|
||||
* True if the app producing the code is too old.
|
||||
* False if the scanning app is too old.
|
||||
*/
|
||||
@Nullable
|
||||
final Boolean qrCodeTooOld;
|
||||
|
||||
Failed(@Nullable Boolean qrCodeTooOld) {
|
||||
this.qrCodeTooOld = qrCodeTooOld;
|
||||
static class WrongQrCodeType extends Failed {
|
||||
|
||||
final QrCodeType qrCodeType;
|
||||
|
||||
WrongQrCodeType(QrCodeType qrCodeType) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
}
|
||||
}
|
||||
|
||||
Failed() {
|
||||
this(null);
|
||||
static class WrongQrCodeVersion extends Failed {
|
||||
|
||||
/**
|
||||
* True if the app producing the code is too old.
|
||||
* False if the scanning app is too old.
|
||||
*/
|
||||
final boolean qrCodeTooOld;
|
||||
|
||||
WrongQrCodeVersion(boolean qrCodeTooOld) {
|
||||
this.qrCodeTooOld = qrCodeTooOld;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class ContactExchangeResult {
|
||||
|
||||
static class Success extends ContactExchangeResult {
|
||||
|
||||
final Author remoteAuthor;
|
||||
|
||||
Success(Author remoteAuthor) {
|
||||
@@ -64,6 +76,7 @@ abstract class AddContactState {
|
||||
}
|
||||
|
||||
static class Error extends ContactExchangeResult {
|
||||
|
||||
@Nullable
|
||||
final Author duplicateAuthor;
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@ import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
@@ -34,6 +37,7 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
|
||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||
|
||||
@@ -141,9 +145,15 @@ public class AddNearbyContactActivity extends BriarActivity
|
||||
ContactExchangeResult result =
|
||||
((ContactExchangeFinished) state).result;
|
||||
onContactExchangeResult(result);
|
||||
} else if (state instanceof WrongQrCodeType) {
|
||||
QrCodeType qrCodeType = ((WrongQrCodeType) state).qrCodeType;
|
||||
if (qrCodeType == MAILBOX) onMailboxQrCodeScanned();
|
||||
else onWrongQrCodeType();
|
||||
} else if (state instanceof WrongQrCodeVersion) {
|
||||
boolean qrCodeTooOld = ((WrongQrCodeVersion) state).qrCodeTooOld;
|
||||
onWrongQrCodeVersion(qrCodeTooOld);
|
||||
} else if (state instanceof Failed) {
|
||||
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld;
|
||||
onAddingContactFailed(qrCodeTooOld);
|
||||
showErrorFragment();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,15 +180,27 @@ public class AddNearbyContactActivity extends BriarActivity
|
||||
} else throw new AssertionError();
|
||||
}
|
||||
|
||||
private void onAddingContactFailed(@Nullable Boolean qrCodeTooOld) {
|
||||
if (qrCodeTooOld == null) {
|
||||
showErrorFragment();
|
||||
} else {
|
||||
String msg;
|
||||
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
|
||||
else msg = getString(R.string.qr_code_too_new_1);
|
||||
showNextFragment(AddNearbyContactErrorFragment.newInstance(msg));
|
||||
}
|
||||
private void onMailboxQrCodeScanned() {
|
||||
String title = getString(R.string.qr_code_invalid);
|
||||
String msg = getString(R.string.mailbox_qr_code_for_contact);
|
||||
showNextFragment(
|
||||
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||
}
|
||||
|
||||
private void onWrongQrCodeType() {
|
||||
String title = getString(R.string.qr_code_invalid);
|
||||
String msg = getString(R.string.qr_code_format_unknown);
|
||||
showNextFragment(
|
||||
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||
}
|
||||
|
||||
private void onWrongQrCodeVersion(boolean qrCodeTooOld) {
|
||||
String title = getString(R.string.qr_code_invalid);
|
||||
String msg;
|
||||
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
|
||||
else msg = getString(R.string.qr_code_too_new_1);
|
||||
showNextFragment(
|
||||
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||
}
|
||||
|
||||
private void showErrorFragment() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.view.View.GONE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||
|
||||
@@ -31,17 +32,22 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
|
||||
|
||||
public static final String TAG =
|
||||
AddNearbyContactErrorFragment.class.getName();
|
||||
private static final String ERROR_MSG = "errorMessage";
|
||||
private static final String ARG_TITLE = "title";
|
||||
private static final String ARG_ERROR_MSG = "message";
|
||||
private static final String ARG_FEEDBACK = "feedback";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private AddNearbyContactViewModel viewModel;
|
||||
|
||||
public static AddNearbyContactErrorFragment newInstance(String errorMsg) {
|
||||
public static AddNearbyContactErrorFragment newInstance(String title,
|
||||
String errorMessage, boolean feedback) {
|
||||
AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ERROR_MSG, errorMsg);
|
||||
args.putString(ARG_TITLE, title);
|
||||
args.putString(ARG_ERROR_MSG, errorMessage);
|
||||
args.putBoolean(ARG_FEEDBACK, feedback);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
@@ -66,19 +72,32 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
|
||||
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
||||
container, false);
|
||||
|
||||
// set optional error message
|
||||
TextView explanation = v.findViewById(R.id.errorMessage);
|
||||
String title = null, errorMessage = null;
|
||||
boolean feedback = true;
|
||||
Bundle args = getArguments();
|
||||
String errorMessage = args == null ? null : args.getString(ERROR_MSG);
|
||||
if (errorMessage == null) {
|
||||
explanation.setText(getString(R.string.add_contact_error_two_way));
|
||||
} else {
|
||||
explanation.setText(args.getString(ERROR_MSG));
|
||||
if (args != null) {
|
||||
title = args.getString(ARG_TITLE);
|
||||
errorMessage = args.getString(ARG_ERROR_MSG);
|
||||
feedback = args.getBoolean(ARG_FEEDBACK, true);
|
||||
}
|
||||
|
||||
if (title != null) {
|
||||
TextView titleView = v.findViewById(R.id.errorTitle);
|
||||
titleView.setText(title);
|
||||
}
|
||||
|
||||
if (errorMessage != null) {
|
||||
TextView messageView = v.findViewById(R.id.errorMessage);
|
||||
messageView.setText(errorMessage);
|
||||
}
|
||||
|
||||
// make feedback link clickable
|
||||
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
||||
onSingleLinkClick(sendFeedback, this::triggerFeedback);
|
||||
if (feedback) {
|
||||
// make feedback link clickable
|
||||
onSingleLinkClick(sendFeedback, this::triggerFeedback);
|
||||
} else {
|
||||
sendFeedback.setVisibility(GONE);
|
||||
}
|
||||
|
||||
// buttons
|
||||
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
|
||||
import org.briarproject.briar.R;
|
||||
@@ -50,9 +51,13 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Contact
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting;
|
||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.QrCodeScanned;
|
||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||
import org.briarproject.briar.android.qrcode.QrCodeUtils;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
@@ -60,7 +65,6 @@ import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -85,6 +89,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted;
|
||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER;
|
||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||
@@ -126,9 +131,6 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
REFUSED
|
||||
}
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Executor ioExecutor;
|
||||
@@ -376,11 +378,11 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
} else if (e instanceof KeyAgreementAbortedEvent) {
|
||||
LOG.info("KeyAgreementAbortedEvent received");
|
||||
resetPayloadFlags();
|
||||
state.setValue(new AddContactState.Failed());
|
||||
state.setValue(new Failed());
|
||||
} else if (e instanceof KeyAgreementFailedEvent) {
|
||||
LOG.info("KeyAgreementFailedEvent received");
|
||||
resetPayloadFlags();
|
||||
state.setValue(new AddContactState.Failed());
|
||||
state.setValue(new Failed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,22 +448,22 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
||||
// Ignore results until the KeyAgreementTask is ready
|
||||
if (!gotLocalPayload || gotRemotePayload || currentTask == null) return;
|
||||
try {
|
||||
byte[] payloadBytes = result.getText().getBytes(ISO_8859_1);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Remote payload is " + payloadBytes.length + " bytes");
|
||||
Payload remotePayload = payloadParser.parse(payloadBytes);
|
||||
Payload remotePayload = payloadParser.parse(result.getText());
|
||||
gotRemotePayload = true;
|
||||
currentTask.connectAndRunProtocol(remotePayload);
|
||||
state.postValue(new AddContactState.QrCodeScanned());
|
||||
state.postValue(new QrCodeScanned());
|
||||
} catch (WrongQrCodeTypeException e) {
|
||||
resetPayloadFlags();
|
||||
state.postValue(new WrongQrCodeType(e.getQrCodeType()));
|
||||
} catch (UnsupportedVersionException e) {
|
||||
resetPayloadFlags();
|
||||
state.postValue(new AddContactState.Failed(e.isTooOld()));
|
||||
state.postValue(new WrongQrCodeVersion(e.isTooOld()));
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
LOG.log(WARNING, "QR Code Invalid", e);
|
||||
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
|
||||
R.string.qr_code_invalid, LENGTH_LONG).show());
|
||||
resetPayloadFlags();
|
||||
state.postValue(new AddContactState.Failed());
|
||||
state.postValue(new Failed());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@NotNullByDefault
|
||||
public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem, H extends ContactItemViewHolder<I>>
|
||||
public abstract class BaseContactSelectorAdapter<I extends BaseSelectableContactItem, H extends ContactItemViewHolder<I>>
|
||||
extends BaseContactListAdapter<I, H> {
|
||||
|
||||
public BaseContactSelectorAdapter(Context context, Class<I> c,
|
||||
@@ -24,7 +24,7 @@ public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem
|
||||
Collection<ContactId> selected = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
SelectableContactItem item = items.get(i);
|
||||
BaseSelectableContactItem item = items.get(i);
|
||||
if (item.isSelected()) selected.add(item.getContact().getId());
|
||||
}
|
||||
return selected;
|
||||
|
||||
@@ -33,7 +33,7 @@ import static org.briarproject.briar.android.contactselection.ContactSelectorAct
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public abstract class BaseContactSelectorFragment<I extends SelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>>
|
||||
public abstract class BaseContactSelectorFragment<I extends BaseSelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>>
|
||||
extends BaseFragment
|
||||
implements OnContactClickListener<I> {
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import static org.briarproject.briar.android.util.UiUtils.GREY_OUT;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
public class BaseSelectableContactHolder<I extends SelectableContactItem>
|
||||
public abstract class BaseSelectableContactHolder<I extends BaseSelectableContactItem>
|
||||
extends ContactItemViewHolder<I> {
|
||||
|
||||
private final CheckBox checkBox;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.briarproject.briar.android.contactselection;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.briar.android.contact.ContactItem;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public abstract class BaseSelectableContactItem extends ContactItem {
|
||||
|
||||
private boolean selected;
|
||||
|
||||
public BaseSelectableContactItem(Contact contact, AuthorInfo authorInfo,
|
||||
boolean selected) {
|
||||
super(contact, authorInfo);
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
void toggleSelected() {
|
||||
selected = !selected;
|
||||
}
|
||||
|
||||
public abstract boolean isDisabled();
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import java.util.Collection;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface ContactSelectorController<I extends SelectableContactItem>
|
||||
public interface ContactSelectorController<I extends BaseSelectableContactItem>
|
||||
extends DbController {
|
||||
|
||||
void loadContacts(GroupId g, Collection<ContactId> selection,
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.briar.android.controller.DbControllerImpl;
|
||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
import org.briarproject.briar.api.identity.AuthorManager;
|
||||
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -53,10 +54,8 @@ public abstract class ContactSelectorControllerImpl
|
||||
AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
|
||||
// was this contact already selected?
|
||||
boolean selected = selection.contains(c.getId());
|
||||
// can this contact be selected?
|
||||
boolean disabled = isDisabled(g, c);
|
||||
contacts.add(new SelectableContactItem(c, authorInfo,
|
||||
selected, disabled));
|
||||
selected, getSharingStatus(g, c)));
|
||||
}
|
||||
handler.onResult(contacts);
|
||||
} catch (DbException e) {
|
||||
@@ -67,7 +66,7 @@ public abstract class ContactSelectorControllerImpl
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract boolean isDisabled(GroupId g, Contact c)
|
||||
protected abstract SharingStatus getSharingStatus(GroupId g, Contact c)
|
||||
throws DbException;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,15 +2,22 @@ package org.briarproject.briar.android.contactselection;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.ERROR;
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_RECEIVED;
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_SENT;
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.NOT_SUPPORTED;
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
@@ -27,6 +34,19 @@ class SelectableContactHolder
|
||||
super.bind(item, listener);
|
||||
|
||||
if (item.isDisabled()) {
|
||||
@StringRes int strRes;
|
||||
if (item.getSharingStatus() == SHARING) {
|
||||
strRes = R.string.forum_invitation_already_sharing;
|
||||
} else if (item.getSharingStatus() == INVITE_SENT) {
|
||||
strRes = R.string.forum_invitation_already_invited;
|
||||
} else if (item.getSharingStatus() == INVITE_RECEIVED) {
|
||||
strRes = R.string.forum_invitation_invite_received;
|
||||
} else if (item.getSharingStatus() == NOT_SUPPORTED) {
|
||||
strRes = R.string.forum_invitation_not_supported;
|
||||
} else if (item.getSharingStatus() == ERROR) {
|
||||
strRes = R.string.forum_invitation_error;
|
||||
} else throw new AssertionError("Unhandled SharingStatus");
|
||||
info.setText(strRes);
|
||||
info.setVisibility(VISIBLE);
|
||||
} else {
|
||||
info.setVisibility(GONE);
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
package org.briarproject.briar.android.contactselection;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.briar.android.contact.ContactItem;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public class SelectableContactItem extends ContactItem {
|
||||
public class SelectableContactItem extends BaseSelectableContactItem {
|
||||
|
||||
private boolean selected;
|
||||
private final boolean disabled;
|
||||
private final SharingStatus sharingStatus;
|
||||
|
||||
public SelectableContactItem(Contact contact, AuthorInfo authorInfo,
|
||||
boolean selected, boolean disabled) {
|
||||
super(contact, authorInfo);
|
||||
this.selected = selected;
|
||||
this.disabled = disabled;
|
||||
boolean selected, SharingStatus sharingStatus) {
|
||||
super(contact, authorInfo, selected);
|
||||
this.sharingStatus = sharingStatus;
|
||||
}
|
||||
|
||||
boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
void toggleSelected() {
|
||||
selected = !selected;
|
||||
public SharingStatus getSharingStatus() {
|
||||
return sharingStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
return disabled;
|
||||
return sharingStatus != SHAREABLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import org.briarproject.briar.android.view.TextAttachmentController.AttachmentLi
|
||||
import org.briarproject.briar.android.view.TextInputView;
|
||||
import org.briarproject.briar.android.view.TextSendController;
|
||||
import org.briarproject.briar.android.view.TextSendController.SendState;
|
||||
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
||||
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
|
||||
@@ -476,6 +477,12 @@ public class ConversationActivity extends BriarActivity
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinkClick(String url) {
|
||||
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
|
||||
f.show(getSupportFragmentManager(), f.getUniqueTag());
|
||||
}
|
||||
|
||||
private void addSelectionTracker() {
|
||||
RecyclerView recyclerView = list.getRecyclerView();
|
||||
if (recyclerView.getAdapter() != adapter)
|
||||
@@ -925,6 +932,7 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
private void removeContact() {
|
||||
list.showProgressBar();
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
contactManager.removeContact(contactId);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
@@ -19,6 +20,7 @@ import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.util.StringUtils.trim;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
||||
|
||||
@UiThread
|
||||
@@ -58,6 +60,8 @@ abstract class ConversationItemViewHolder extends ViewHolder {
|
||||
|
||||
if (item.getText() != null) {
|
||||
text.setText(trim(item.getText()));
|
||||
Linkify.addLinks(text, Linkify.WEB_URLS);
|
||||
makeLinksClickable(text, listener::onLinkClick);
|
||||
}
|
||||
|
||||
long timestamp = item.getTime();
|
||||
|
||||
@@ -20,4 +20,6 @@ interface ConversationListener {
|
||||
|
||||
void onAutoDeleteTimerNoticeClicked();
|
||||
|
||||
void onLinkClick(String url);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.text.util.Linkify;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -13,6 +14,7 @@ import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.bramble.util.StringUtils.trim;
|
||||
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
@@ -40,6 +42,8 @@ class ConversationNoticeViewHolder extends ConversationItemViewHolder {
|
||||
} else {
|
||||
msgText.setVisibility(VISIBLE);
|
||||
msgText.setText(trim(text));
|
||||
Linkify.addLinks(msgText, Linkify.WEB_URLS);
|
||||
makeLinksClickable(msgText, listener::onLinkClick);
|
||||
layout.setBackgroundResource(isIncoming() ?
|
||||
R.drawable.notice_in_bottom : R.drawable.notice_out_bottom);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.briarproject.briar.android.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ProgressFragment extends BaseFragment {
|
||||
|
||||
public static final String TAG = ProgressFragment.class.getName();
|
||||
|
||||
private static final String PROGRESS_MSG = "progressMessage";
|
||||
|
||||
public static ProgressFragment newInstance(String message) {
|
||||
ProgressFragment f = new ProgressFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(PROGRESS_MSG, message);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
private String progressMessage;
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle args = requireArguments();
|
||||
progressMessage = args.getString(PROGRESS_MSG);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View v = inflater
|
||||
.inflate(R.layout.fragment_progress, container, false);
|
||||
TextView msg = v.findViewById(R.id.progressMessage);
|
||||
msg.setText(progressMessage);
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -436,7 +436,7 @@ class HotspotManager {
|
||||
}
|
||||
|
||||
private static String createWifiLoginString(String ssid, String password) {
|
||||
// https://en.wikipedia.org/wiki/QR_code#WiFi_network_login
|
||||
// https://en.wikipedia.org/wiki/QR_code#Joining_a_Wi%E2%80%91Fi_network
|
||||
// do not remove the dangling ';', it can cause problems to omit it
|
||||
return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
|
||||
}
|
||||
|
||||
@@ -6,10 +6,24 @@ import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pending;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.FinalFragment;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.CameraError;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.IsPaired;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.OfflineWhenPairing;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.Pairing;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.ScanningQrCode;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.ShowDownload;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.WasUnpaired;
|
||||
import org.briarproject.briar.android.view.BlankFragment;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
@@ -25,6 +39,9 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -56,24 +73,23 @@ public class MailboxActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
viewModel.getPairingState().observeEvent(this, state -> {
|
||||
if (state instanceof MailboxState.NotSetup) {
|
||||
if (state instanceof NotSetup) {
|
||||
onNotSetup();
|
||||
} else if (state instanceof MailboxState.ShowDownload) {
|
||||
} else if (state instanceof ShowDownload) {
|
||||
onShowDownload();
|
||||
} else if (state instanceof MailboxState.ScanningQrCode) {
|
||||
} else if (state instanceof ScanningQrCode) {
|
||||
onScanningQrCode();
|
||||
} else if (state instanceof MailboxState.Pairing) {
|
||||
MailboxPairingState s =
|
||||
((MailboxState.Pairing) state).pairingState;
|
||||
} else if (state instanceof Pairing) {
|
||||
MailboxPairingState s = ((Pairing) state).pairingState;
|
||||
onMailboxPairingStateChanged(s);
|
||||
} else if (state instanceof MailboxState.OfflineWhenPairing) {
|
||||
} else if (state instanceof OfflineWhenPairing) {
|
||||
onOffline();
|
||||
} else if (state instanceof MailboxState.CameraError) {
|
||||
} else if (state instanceof CameraError) {
|
||||
onCameraError();
|
||||
} else if (state instanceof MailboxState.IsPaired) {
|
||||
onIsPaired(((MailboxState.IsPaired) state).isOnline);
|
||||
} else if (state instanceof MailboxState.WasUnpaired) {
|
||||
MailboxState.WasUnpaired s = (MailboxState.WasUnpaired) state;
|
||||
} else if (state instanceof IsPaired) {
|
||||
onIsPaired(((IsPaired) state).isOnline);
|
||||
} else if (state instanceof WasUnpaired) {
|
||||
WasUnpaired s = (WasUnpaired) state;
|
||||
onUnPaired(s.tellUserToWipeMailbox);
|
||||
} else {
|
||||
throw new AssertionError("Unknown state: " + state);
|
||||
@@ -104,7 +120,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
MailboxState s = viewModel.getPairingState().getLastValue();
|
||||
if (s instanceof MailboxState.Pairing) {
|
||||
if (s instanceof Pairing) {
|
||||
// don't go back in the flow if we are already pairing
|
||||
// with the mailbox. We provide a try-again button instead.
|
||||
supportFinishAfterTransition();
|
||||
@@ -158,33 +174,44 @@ public class MailboxActivity extends BriarActivity {
|
||||
}
|
||||
Fragment f;
|
||||
String tag;
|
||||
if (s instanceof MailboxPairingState.QrCodeReceived) {
|
||||
f = new MailboxConnectingFragment();
|
||||
if (s instanceof Pending) {
|
||||
long timeStarted = ((Pending) s).timeStarted;
|
||||
f = MailboxConnectingFragment.newInstance(timeStarted);
|
||||
tag = MailboxConnectingFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.Pairing) {
|
||||
f = new MailboxConnectingFragment();
|
||||
tag = MailboxConnectingFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.InvalidQrCode) {
|
||||
f = ErrorFragment.newInstance(
|
||||
R.string.mailbox_setup_qr_code_wrong_title,
|
||||
R.string.mailbox_setup_qr_code_wrong_description);
|
||||
} else if (s instanceof InvalidQrCode) {
|
||||
InvalidQrCode i = (InvalidQrCode) s;
|
||||
int errorRes;
|
||||
if (i.qrCodeType == MAILBOX) {
|
||||
if (i.formatVersion < QR_FORMAT_VERSION) {
|
||||
errorRes = R.string.mailbox_qr_code_too_old;
|
||||
} else if (i.formatVersion > QR_FORMAT_VERSION) {
|
||||
errorRes = R.string.mailbox_qr_code_too_new;
|
||||
} else {
|
||||
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
|
||||
}
|
||||
} else if (i.qrCodeType == BQP) {
|
||||
errorRes = R.string.contact_qr_code_for_mailbox;
|
||||
} else {
|
||||
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
|
||||
}
|
||||
f = ErrorFragment.newInstance(R.string.qr_code_invalid, errorRes);
|
||||
tag = ErrorFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.MailboxAlreadyPaired) {
|
||||
} else if (s instanceof MailboxAlreadyPaired) {
|
||||
f = ErrorFragment.newInstance(
|
||||
R.string.mailbox_setup_already_paired_title,
|
||||
R.string.mailbox_setup_already_paired_description);
|
||||
tag = ErrorFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.ConnectionError) {
|
||||
} else if (s instanceof ConnectionError) {
|
||||
f = ErrorFragment.newInstance(
|
||||
R.string.mailbox_setup_io_error_title,
|
||||
R.string.mailbox_setup_io_error_description);
|
||||
tag = ErrorFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.UnexpectedError) {
|
||||
} else if (s instanceof UnexpectedError) {
|
||||
f = ErrorFragment.newInstance(
|
||||
R.string.mailbox_setup_assertion_error_title,
|
||||
R.string.mailbox_setup_assertion_error_description);
|
||||
tag = ErrorFragment.TAG;
|
||||
} else if (s instanceof MailboxPairingState.Paired) {
|
||||
} else if (s instanceof Paired) {
|
||||
f = FinalFragment.newInstance(R.string.mailbox_setup_paired_title,
|
||||
R.drawable.ic_check_circle_outline,
|
||||
R.color.briar_brand_green,
|
||||
@@ -218,6 +245,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
private void onUnPaired(boolean tellUserToWipeMailbox) {
|
||||
viewModel.clearProblemNotification();
|
||||
if (tellUserToWipeMailbox) {
|
||||
showFragment(getSupportFragmentManager(), new BlankFragment(),
|
||||
BlankFragment.TAG);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
@@ -12,25 +17,71 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class MailboxConnectingFragment extends Fragment {
|
||||
|
||||
static final String TAG = MailboxConnectingFragment.class.getName();
|
||||
|
||||
private static final String ARG_STARTED = "started";
|
||||
private static final long TIMEOUT_MS = TorConstants.EXTRA_CONNECT_TIMEOUT;
|
||||
private static final long REFRESH_INTERVAL_MS = 1_000;
|
||||
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
// Capture a method reference so we use the same reference for posting
|
||||
// and removing
|
||||
private final Runnable refresher = this::updateProgressBar;
|
||||
|
||||
private ProgressBar progressBar;
|
||||
private long timeStarted;
|
||||
|
||||
public static MailboxConnectingFragment newInstance(long timeStarted) {
|
||||
MailboxConnectingFragment f = new MailboxConnectingFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putLong(ARG_STARTED, timeStarted);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_mailbox_connecting,
|
||||
View v = inflater.inflate(R.layout.fragment_mailbox_connecting,
|
||||
container, false);
|
||||
|
||||
progressBar = v.findViewById(R.id.progressBar);
|
||||
TextView info = v.findViewById(R.id.info);
|
||||
String duration = formatDuration(requireContext(), TIMEOUT_MS);
|
||||
info.setText(getString(R.string.mailbox_setup_connecting_info,
|
||||
duration));
|
||||
|
||||
timeStarted = requireArguments().getLong(ARG_STARTED);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.mailbox_setup_title);
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
handler.removeCallbacks(refresher);
|
||||
}
|
||||
|
||||
private void updateProgressBar() {
|
||||
long elapsedMs = System.currentTimeMillis() - timeStarted;
|
||||
int percent = (int) (elapsedMs * 100 / TIMEOUT_MS);
|
||||
percent = Math.min(Math.max(percent, 0), 100);
|
||||
progressBar.setProgress(percent);
|
||||
handler.postDelayed(refresher, REFRESH_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user