mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
18 Commits
release-1.
...
beta-1.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5563ead28 | ||
|
|
e15f49fde7 | ||
|
|
1531a24b2d | ||
|
|
2298818af5 | ||
|
|
0ae5361281 | ||
|
|
d8e26eebbe | ||
|
|
692e353046 | ||
|
|
b9ba7aded5 | ||
|
|
4bca9decc1 | ||
|
|
7bbe9068bb | ||
|
|
63060679a3 | ||
|
|
ddb759dbb8 | ||
|
|
feb8854678 | ||
|
|
1af52b21d5 | ||
|
|
e481a02126 | ||
|
|
825dff27fc | ||
|
|
de3a87fff5 | ||
|
|
85d1addd04 |
@@ -15,8 +15,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10409
|
||||
versionName "1.4.9"
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -37,8 +37,14 @@ public interface LifecycleManager {
|
||||
*/
|
||||
enum LifecycleState {
|
||||
|
||||
STARTING, MIGRATING_DATABASE, COMPACTING_DATABASE, STARTING_SERVICES,
|
||||
RUNNING, STOPPING;
|
||||
CREATED,
|
||||
STARTING,
|
||||
MIGRATING_DATABASE,
|
||||
COMPACTING_DATABASE,
|
||||
STARTING_SERVICES,
|
||||
RUNNING,
|
||||
STOPPING,
|
||||
STOPPED;
|
||||
|
||||
public boolean isAfter(LifecycleState state) {
|
||||
return ordinal() > state.ordinal();
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -32,7 +33,7 @@ public abstract class MailboxId extends UniqueId {
|
||||
}
|
||||
try {
|
||||
return fromHexString(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (FormatException e) {
|
||||
throw new InvalidMailboxIdException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class MailboxProperties {
|
||||
|
||||
private final String baseUrl;
|
||||
private final String onion;
|
||||
private final MailboxAuthToken authToken;
|
||||
private final boolean owner;
|
||||
private final List<MailboxVersion> serverSupports;
|
||||
@@ -23,9 +23,9 @@ public class MailboxProperties {
|
||||
/**
|
||||
* Constructor for properties used by the mailbox's owner.
|
||||
*/
|
||||
public MailboxProperties(String baseUrl, MailboxAuthToken authToken,
|
||||
public MailboxProperties(String onion, MailboxAuthToken authToken,
|
||||
List<MailboxVersion> serverSupports) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.onion = onion;
|
||||
this.authToken = authToken;
|
||||
this.owner = true;
|
||||
this.serverSupports = serverSupports;
|
||||
@@ -36,10 +36,10 @@ public class MailboxProperties {
|
||||
/**
|
||||
* Constructor for properties used by a contact of the mailbox's owner.
|
||||
*/
|
||||
public MailboxProperties(String baseUrl, MailboxAuthToken authToken,
|
||||
public MailboxProperties(String onion, MailboxAuthToken authToken,
|
||||
List<MailboxVersion> serverSupports, MailboxFolderId inboxId,
|
||||
MailboxFolderId outboxId) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.onion = onion;
|
||||
this.authToken = authToken;
|
||||
this.owner = false;
|
||||
this.serverSupports = serverSupports;
|
||||
@@ -47,13 +47,11 @@ public class MailboxProperties {
|
||||
this.outboxId = outboxId;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the onion address of the mailbox, excluding the .onion suffix.
|
||||
*/
|
||||
public String getOnion() {
|
||||
return baseUrl.replaceFirst("^http://", "")
|
||||
.replaceFirst("\\.onion$", "");
|
||||
return onion;
|
||||
}
|
||||
|
||||
public MailboxAuthToken getAuthToken() {
|
||||
|
||||
@@ -29,19 +29,35 @@ public interface MailboxUpdateManager {
|
||||
|
||||
/**
|
||||
* The number of properties required for an update message with a mailbox.
|
||||
* <p>
|
||||
* The required properties are {@link #PROP_KEY_ONION},
|
||||
* {@link #PROP_KEY_AUTHTOKEN}, {@link #PROP_KEY_INBOXID} and
|
||||
* {@link #PROP_KEY_OUTBOXID}.
|
||||
*/
|
||||
int PROP_COUNT = 4;
|
||||
|
||||
/**
|
||||
* The required properties of an update message with a mailbox.
|
||||
* The onion address of the mailbox, excluding the .onion suffix.
|
||||
*/
|
||||
String PROP_KEY_ONION = "onion";
|
||||
|
||||
/**
|
||||
* A bearer token for accessing the mailbox (64 hex digits).
|
||||
*/
|
||||
String PROP_KEY_AUTHTOKEN = "authToken";
|
||||
|
||||
/**
|
||||
* A folder ID for downloading messages (64 hex digits).
|
||||
*/
|
||||
String PROP_KEY_INBOXID = "inboxId";
|
||||
|
||||
/**
|
||||
* A folder ID for uploading messages (64 hex digits).
|
||||
*/
|
||||
String PROP_KEY_OUTBOXID = "outboxId";
|
||||
|
||||
/**
|
||||
* Length of the Onion property.
|
||||
* Length of the {@link #PROP_KEY_ONION} property.
|
||||
*/
|
||||
int PROP_ONION_LENGTH = 56;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -95,10 +96,10 @@ public class StringUtils {
|
||||
/**
|
||||
* Converts the given hex string to a byte array.
|
||||
*/
|
||||
public static byte[] fromHexString(String hex) {
|
||||
public static byte[] fromHexString(String hex) throws FormatException {
|
||||
int len = hex.length();
|
||||
if (len % 2 != 0)
|
||||
throw new IllegalArgumentException("Not a hex string");
|
||||
throw new FormatException();
|
||||
byte[] bytes = new byte[len / 2];
|
||||
for (int i = 0, j = 0; i < len; i += 2, j++) {
|
||||
int high = hexDigitToInt(hex.charAt(i));
|
||||
@@ -108,11 +109,11 @@ public class StringUtils {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static int hexDigitToInt(char c) {
|
||||
private static int hexDigitToInt(char c) throws FormatException {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
throw new IllegalArgumentException("Not a hex digit: " + c);
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public static String trim(String s) {
|
||||
@@ -130,13 +131,13 @@ public class StringUtils {
|
||||
return MAC.matcher(mac).matches();
|
||||
}
|
||||
|
||||
public static byte[] macToBytes(String mac) {
|
||||
if (!MAC.matcher(mac).matches()) throw new IllegalArgumentException();
|
||||
public static byte[] macToBytes(String mac) throws FormatException {
|
||||
if (!MAC.matcher(mac).matches()) throw new FormatException();
|
||||
return fromHexString(mac.replaceAll(":", ""));
|
||||
}
|
||||
|
||||
public static String macToString(byte[] mac) {
|
||||
if (mac.length != 6) throw new IllegalArgumentException();
|
||||
public static String macToString(byte[] mac) throws FormatException {
|
||||
if (mac.length != 6) throw new FormatException();
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (byte b : mac) {
|
||||
if (s.length() > 0) s.append(':');
|
||||
|
||||
@@ -230,14 +230,14 @@ public class TestUtils {
|
||||
|
||||
public static MailboxProperties getMailboxProperties(boolean owner,
|
||||
List<MailboxVersion> serverSupports) {
|
||||
String baseUrl = "http://" + getRandomString(56) + ".onion"; // TODO
|
||||
String onion = getRandomString(56);
|
||||
MailboxAuthToken authToken = new MailboxAuthToken(getRandomId());
|
||||
if (owner) {
|
||||
return new MailboxProperties(baseUrl, authToken, serverSupports);
|
||||
return new MailboxProperties(onion, authToken, serverSupports);
|
||||
}
|
||||
MailboxFolderId inboxId = new MailboxFolderId(getRandomId());
|
||||
MailboxFolderId outboxId = new MailboxFolderId(getRandomId());
|
||||
return new MailboxProperties(baseUrl, authToken, serverSupports,
|
||||
return new MailboxProperties(onion, authToken, serverSupports,
|
||||
inboxId, outboxId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.account;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.DecryptionException;
|
||||
@@ -209,7 +210,13 @@ class AccountManagerImpl implements AccountManager {
|
||||
LOG.warning("Failed to load encrypted database key");
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
byte[] ciphertext = fromHexString(hex);
|
||||
byte[] ciphertext;
|
||||
try {
|
||||
ciphertext = fromHexString(hex);
|
||||
} catch (FormatException e) {
|
||||
LOG.warning("Encrypted database key has invalid format");
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener();
|
||||
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
|
||||
keyStrengthener);
|
||||
|
||||
@@ -456,8 +456,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
checkLength(inboxId, UniqueId.LENGTH);
|
||||
byte[] outboxId = properties.getRaw(PROP_KEY_OUTBOXID);
|
||||
checkLength(outboxId, UniqueId.LENGTH);
|
||||
String baseUrl = "http://" + onion + ".onion"; // TODO
|
||||
MailboxProperties props = new MailboxProperties(baseUrl,
|
||||
MailboxProperties props = new MailboxProperties(onion,
|
||||
new MailboxAuthToken(authToken), serverSupportsList,
|
||||
new MailboxFolderId(inboxId), new MailboxFolderId(outboxId));
|
||||
return new MailboxUpdateWithMailbox(clientSupportsList, props);
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
@@ -29,10 +29,12 @@ import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.CREATED;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPED;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
|
||||
@@ -60,12 +62,11 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
private final List<Service> services;
|
||||
private final List<OpenDatabaseHook> openDatabaseHooks;
|
||||
private final List<ExecutorService> executors;
|
||||
private final Semaphore startStopSemaphore = new Semaphore(1);
|
||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch startupLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
|
||||
|
||||
private volatile LifecycleState state = STARTING;
|
||||
private final AtomicReference<LifecycleState> state =
|
||||
new AtomicReference<>(CREATED);
|
||||
|
||||
@Inject
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||
@@ -102,8 +103,8 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
@Override
|
||||
public StartResult startServices(SecretKey dbKey) {
|
||||
if (!startStopSemaphore.tryAcquire()) {
|
||||
LOG.info("Already starting or stopping");
|
||||
if (!state.compareAndSet(CREATED, STARTING)) {
|
||||
LOG.warning("Already running");
|
||||
return ALREADY_RUNNING;
|
||||
}
|
||||
long now = clock.currentTimeMillis();
|
||||
@@ -135,7 +136,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
});
|
||||
|
||||
LOG.info("Starting services");
|
||||
state = STARTING_SERVICES;
|
||||
state.set(STARTING_SERVICES);
|
||||
dbLatch.countDown();
|
||||
eventBus.broadcast(new LifecycleEvent(STARTING_SERVICES));
|
||||
|
||||
@@ -148,7 +149,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
}
|
||||
}
|
||||
|
||||
state = RUNNING;
|
||||
state.set(RUNNING);
|
||||
startupLatch.countDown();
|
||||
eventBus.broadcast(new LifecycleEvent(RUNNING));
|
||||
return SUCCESS;
|
||||
@@ -164,63 +165,58 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
} catch (ServiceException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return SERVICE_ERROR;
|
||||
} finally {
|
||||
startStopSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseMigration() {
|
||||
state = MIGRATING_DATABASE;
|
||||
state.set(MIGRATING_DATABASE);
|
||||
eventBus.broadcast(new LifecycleEvent(MIGRATING_DATABASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseCompaction() {
|
||||
state = COMPACTING_DATABASE;
|
||||
state.set(COMPACTING_DATABASE);
|
||||
eventBus.broadcast(new LifecycleEvent(COMPACTING_DATABASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopServices() {
|
||||
try {
|
||||
startStopSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while waiting to stop services");
|
||||
if (!state.compareAndSet(RUNNING, STOPPING)) {
|
||||
LOG.warning("Not running");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (state == STOPPING) {
|
||||
LOG.info("Already stopped");
|
||||
return;
|
||||
}
|
||||
LOG.info("Stopping services");
|
||||
state = STOPPING;
|
||||
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
||||
for (Service s : services) {
|
||||
LOG.info("Stopping services");
|
||||
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
||||
for (Service s : services) {
|
||||
try {
|
||||
long start = now();
|
||||
s.stopService();
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
logDuration(LOG, "Stopping service "
|
||||
+ s.getClass().getSimpleName(), start);
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
for (ExecutorService e : executors) {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine("Stopping executor "
|
||||
+ e.getClass().getSimpleName());
|
||||
}
|
||||
e.shutdownNow();
|
||||
}
|
||||
for (ExecutorService e : executors) {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine("Stopping executor "
|
||||
+ e.getClass().getSimpleName());
|
||||
}
|
||||
e.shutdownNow();
|
||||
}
|
||||
try {
|
||||
long start = now();
|
||||
db.close();
|
||||
logDuration(LOG, "Closing database", start);
|
||||
shutdownLatch.countDown();
|
||||
} catch (DbException | ServiceException e) {
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
} finally {
|
||||
startStopSemaphore.release();
|
||||
}
|
||||
state.set(STOPPED);
|
||||
shutdownLatch.countDown();
|
||||
eventBus.broadcast(new LifecycleEvent(STOPPED));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,6 +236,6 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
@Override
|
||||
public LifecycleState getLifecycleState() {
|
||||
return state;
|
||||
return state.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,18 +42,22 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
@NotNullByDefault
|
||||
class MailboxApiImpl implements MailboxApi {
|
||||
|
||||
private final WeakSingletonProvider<OkHttpClient> httpClientProvider;
|
||||
private final JsonMapper mapper = JsonMapper.builder()
|
||||
.enable(BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES)
|
||||
.build();
|
||||
private static final MediaType JSON =
|
||||
requireNonNull(MediaType.parse("application/json; charset=utf-8"));
|
||||
private static final MediaType FILE =
|
||||
requireNonNull(MediaType.parse("application/octet-stream"));
|
||||
|
||||
private final WeakSingletonProvider<OkHttpClient> httpClientProvider;
|
||||
private final JsonMapper mapper = JsonMapper.builder()
|
||||
.enable(BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES)
|
||||
.build();
|
||||
private final UrlConverter urlConverter;
|
||||
|
||||
@Inject
|
||||
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider) {
|
||||
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider,
|
||||
UrlConverter urlConverter) {
|
||||
this.httpClientProvider = httpClientProvider;
|
||||
this.urlConverter = urlConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +82,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
throws IOException, ApiException {
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.url(properties.getBaseUrl() + "/setup")
|
||||
.url(getBaseUrl(properties) + "/setup")
|
||||
.put(EMPTY_REQUEST)
|
||||
.build();
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
@@ -93,7 +97,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
if (tokenNode == null) {
|
||||
throw new ApiException();
|
||||
}
|
||||
return new MailboxProperties(properties.getBaseUrl(),
|
||||
return new MailboxProperties(properties.getOnion(),
|
||||
MailboxAuthToken.fromString(tokenNode.textValue()),
|
||||
parseServerSupports(node));
|
||||
} catch (JacksonException | InvalidMailboxIdException e) {
|
||||
@@ -137,7 +141,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
throws IOException, ApiException {
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.url(properties.getBaseUrl() + "/")
|
||||
.url(getBaseUrl(properties) + "/")
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
@@ -162,7 +166,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
public void deleteContact(MailboxProperties properties, ContactId contactId)
|
||||
throws IOException, ApiException, TolerableFailureException {
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
String url = properties.getBaseUrl() + "/contacts/" +
|
||||
String url = getBaseUrl(properties) + "/contacts/" +
|
||||
contactId.getInt();
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.delete()
|
||||
@@ -266,7 +270,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
String path = "/files/" + folderId + "/" + fileId;
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.delete()
|
||||
.url(properties.getBaseUrl() + path)
|
||||
.url(getBaseUrl(properties) + path)
|
||||
.build();
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
Response response = client.newCall(request).execute();
|
||||
@@ -308,7 +312,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
private Response sendGetRequest(MailboxProperties properties, String path)
|
||||
throws IOException {
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.url(properties.getBaseUrl() + path)
|
||||
.url(getBaseUrl(properties) + path)
|
||||
.build();
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
return client.newCall(request).execute();
|
||||
@@ -317,7 +321,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
private Response sendPostRequest(MailboxProperties properties, String path,
|
||||
RequestBody body) throws IOException {
|
||||
Request request = getRequestBuilder(properties.getAuthToken())
|
||||
.url(properties.getBaseUrl() + path)
|
||||
.url(getBaseUrl(properties) + path)
|
||||
.post(body)
|
||||
.build();
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
@@ -339,4 +343,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
return (ArrayNode) arrayNode;
|
||||
}
|
||||
|
||||
private String getBaseUrl(MailboxProperties properties) {
|
||||
return urlConverter.convertOnionToBaseUrl(properties.getOnion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,12 @@ public class MailboxModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
MailboxApi providesMailboxApi(MailboxApiImpl mailboxApi) {
|
||||
UrlConverter provideUrlConverter(UrlConverterImpl urlConverter) {
|
||||
return urlConverter;
|
||||
}
|
||||
|
||||
@Provides
|
||||
MailboxApi provideMailboxApi(MailboxApiImpl mailboxApi) {
|
||||
return mailboxApi;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,10 +177,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
LOG.info("QR code is valid");
|
||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||
String onion = crypto.encodeOnion(onionPubKey);
|
||||
String baseUrl = "http://" + onion + ".onion"; // TODO
|
||||
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
|
||||
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
|
||||
return new MailboxProperties(baseUrl, setupToken, new ArrayList<>());
|
||||
return new MailboxProperties(onion, setupToken, new ArrayList<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
public void setOwnMailboxProperties(Transaction txn, MailboxProperties p)
|
||||
throws DbException {
|
||||
Settings s = new Settings();
|
||||
s.put(SETTINGS_KEY_ONION, p.getBaseUrl());
|
||||
s.put(SETTINGS_KEY_ONION, p.getOnion());
|
||||
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken().toString());
|
||||
List<MailboxVersion> serverSupports = p.getServerSupports();
|
||||
encodeServerSupports(serverSupports, s);
|
||||
|
||||
@@ -242,8 +242,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
private void createAndSendUpdateWithMailbox(Transaction txn, Contact c,
|
||||
List<MailboxVersion> serverSupports, String ownOnion)
|
||||
throws DbException {
|
||||
String baseUrl = "http://" + ownOnion + ".onion"; // TODO
|
||||
MailboxProperties properties = new MailboxProperties(baseUrl,
|
||||
MailboxProperties properties = new MailboxProperties(ownOnion,
|
||||
new MailboxAuthToken(crypto.generateUniqueId().getBytes()),
|
||||
serverSupports,
|
||||
new MailboxFolderId(crypto.generateUniqueId().getBytes()),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
/**
|
||||
* An interface for converting an onion address to an HTTP URL, allowing the
|
||||
* conversion to be customised for integration tests.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
interface UrlConverter {
|
||||
|
||||
/**
|
||||
* Converts a raw onion address, excluding the .onion suffix, into an
|
||||
* HTTP URL.
|
||||
*/
|
||||
String convertOnionToBaseUrl(String onion);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@NotNullByDefault
|
||||
class UrlConverterImpl implements UrlConverter {
|
||||
|
||||
@Inject
|
||||
UrlConverterImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertOnionToBaseUrl(String onion) {
|
||||
return "http://" + onion + ".onion";
|
||||
}
|
||||
}
|
||||
@@ -427,7 +427,13 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
BdfList descriptor = new BdfList();
|
||||
descriptor.add(TRANSPORT_ID_BLUETOOTH);
|
||||
String address = getBluetoothAddress();
|
||||
if (address != null) descriptor.add(macToBytes(address));
|
||||
if (address != null) {
|
||||
try {
|
||||
descriptor.add(macToBytes(address));
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||
}
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
if (ip.length == 16) addrs.add(InetAddress.getByAddress(ip));
|
||||
}
|
||||
return addrs;
|
||||
} catch (IllegalArgumentException | UnknownHostException e) {
|
||||
} catch (FormatException | UnknownHostException e) {
|
||||
return emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -93,9 +94,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
|
||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
@@ -239,8 +238,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
// Load the settings
|
||||
settings = callback.getSettings();
|
||||
// Install or update the assets if necessary
|
||||
if (!assetsAreUpToDate()) installAssets();
|
||||
try {
|
||||
// Install or update the assets if necessary
|
||||
if (!assetsAreUpToDate()) installAssets();
|
||||
// Start from the default config every time
|
||||
extract(getConfigInputStream(), configFile);
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
if (cookieFile.exists() && !cookieFile.delete())
|
||||
LOG.warning("Old auth cookie not deleted");
|
||||
// Start a new Tor process
|
||||
@@ -302,7 +307,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
info = controlConnection.getInfo("status/circuit-established");
|
||||
if ("1".equals(info)) {
|
||||
LOG.info("Tor has already built a circuit");
|
||||
state.getAndSetCircuitBuilt(true);
|
||||
state.setCircuitBuilt(true);
|
||||
}
|
||||
} catch (TorNotRunningException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -321,25 +326,20 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
return doneFile.lastModified() > getLastUpdateTime();
|
||||
}
|
||||
|
||||
private void installAssets() throws PluginException {
|
||||
try {
|
||||
// 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();
|
||||
extract(getConfigInputStream(), configFile);
|
||||
if (!doneFile.createNewFile())
|
||||
LOG.warning("Failed to create done file");
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
private void installAssets() throws IOException {
|
||||
// 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();
|
||||
if (!doneFile.createNewFile())
|
||||
LOG.warning("Failed to create done file");
|
||||
}
|
||||
|
||||
protected void extract(InputStream in, File dest) throws IOException {
|
||||
@@ -398,6 +398,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
append(strb, "SocksPort", torSocksPort);
|
||||
strb.append("GeoIPFile\n");
|
||||
strb.append("GeoIPv6File\n");
|
||||
append(strb, "ConnectionPadding", 0);
|
||||
String obfs4Path = getObfs4ExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path);
|
||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new ByteArrayInputStream(
|
||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||
@@ -547,7 +551,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
|
||||
protected void enableNetwork(boolean enable) throws IOException {
|
||||
state.enableNetwork(enable);
|
||||
if (!state.enableNetwork(enable)) return; // Unchanged
|
||||
try {
|
||||
controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
|
||||
} catch (TorNotRunningException e) {
|
||||
@@ -555,28 +559,20 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void enableBridges(boolean enable, List<BridgeType> bridgeTypes)
|
||||
private void enableBridges(List<BridgeType> bridgeTypes)
|
||||
throws IOException {
|
||||
if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged
|
||||
try {
|
||||
if (enable) {
|
||||
if (bridgeTypes.isEmpty()) {
|
||||
controlConnection.setConf("UseBridges", "0");
|
||||
controlConnection.resetConf(singletonList("Bridge"));
|
||||
} else {
|
||||
Collection<String> conf = new ArrayList<>();
|
||||
conf.add("UseBridges 1");
|
||||
File obfs4File = getObfs4ExecutableFile();
|
||||
if (bridgeTypes.contains(MEEK)) {
|
||||
conf.add("ClientTransportPlugin meek_lite exec " +
|
||||
obfs4File.getAbsolutePath());
|
||||
}
|
||||
if (bridgeTypes.contains(DEFAULT_OBFS4) ||
|
||||
bridgeTypes.contains(NON_DEFAULT_OBFS4)) {
|
||||
conf.add("ClientTransportPlugin obfs4 exec " +
|
||||
obfs4File.getAbsolutePath());
|
||||
}
|
||||
for (BridgeType bridgeType : bridgeTypes) {
|
||||
conf.addAll(circumventionProvider.getBridges(bridgeType));
|
||||
}
|
||||
controlConnection.setConf(conf);
|
||||
} else {
|
||||
controlConnection.setConf("UseBridges", "0");
|
||||
}
|
||||
} catch (TorNotRunningException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -590,7 +586,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (controlSocket != null && controlConnection != null) {
|
||||
try {
|
||||
LOG.info("Stopping Tor");
|
||||
controlConnection.setConf("DisableNetwork", "1");
|
||||
controlConnection.shutdownTor("TERM");
|
||||
controlSocket.close();
|
||||
} catch (TorNotRunningException e) {
|
||||
@@ -758,7 +753,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
public void circuitStatus(String status, String id, String path) {
|
||||
// In case of races between receiving CIRCUIT_ESTABLISHED and setting
|
||||
// DisableNetwork, set our circuitBuilt flag if not already set
|
||||
if (status.equals("BUILT") && !state.getAndSetCircuitBuilt(true)) {
|
||||
if (status.equals("BUILT") && state.setCircuitBuilt(true)) {
|
||||
LOG.info("Circuit built");
|
||||
backoff.reset();
|
||||
}
|
||||
@@ -815,12 +810,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
state.setBootstrapped();
|
||||
backoff.reset();
|
||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
||||
if (!state.getAndSetCircuitBuilt(true)) {
|
||||
if (state.setCircuitBuilt(true)) {
|
||||
LOG.info("Circuit built");
|
||||
backoff.reset();
|
||||
}
|
||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
||||
if (state.getAndSetCircuitBuilt(false)) {
|
||||
if (state.setCircuitBuilt(false)) {
|
||||
LOG.info("Circuit not built");
|
||||
// TODO: Disable and re-enable network to prompt Tor to rebuild
|
||||
// its guard/bridge connections? This will also close any
|
||||
@@ -911,10 +906,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
|
||||
int reasonsDisabled = 0;
|
||||
boolean enableNetwork = false, enableBridges = false;
|
||||
boolean enableConnectionPadding = false;
|
||||
List<BridgeType> bridgeTypes =
|
||||
circumventionProvider.getSuitableBridgeTypes(country);
|
||||
boolean enableNetwork = false, enableConnectionPadding = false;
|
||||
List<BridgeType> bridgeTypes = emptyList();
|
||||
|
||||
if (!online) {
|
||||
LOG.info("Disabling network, device is offline");
|
||||
@@ -943,8 +936,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
enableNetwork = true;
|
||||
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
|
||||
(automatic && bridgesWork)) {
|
||||
if (ipv6Only) bridgeTypes = singletonList(MEEK);
|
||||
enableBridges = true;
|
||||
if (ipv6Only) {
|
||||
bridgeTypes = singletonList(MEEK);
|
||||
} else {
|
||||
bridgeTypes = circumventionProvider
|
||||
.getSuitableBridgeTypes(country);
|
||||
}
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Using bridge types " + bridgeTypes);
|
||||
}
|
||||
@@ -964,9 +961,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
try {
|
||||
if (enableNetwork) {
|
||||
enableBridges(enableBridges, bridgeTypes);
|
||||
enableBridges(bridgeTypes);
|
||||
enableConnectionPadding(enableConnectionPadding);
|
||||
useIpv6(ipv6Only);
|
||||
enableIpv6(ipv6Only);
|
||||
}
|
||||
enableNetwork(enableNetwork);
|
||||
} catch (IOException e) {
|
||||
@@ -976,6 +973,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
|
||||
private void enableConnectionPadding(boolean enable) throws IOException {
|
||||
if (!state.enableConnectionPadding(enable)) return; // Unchanged
|
||||
try {
|
||||
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
|
||||
} catch (TorNotRunningException e) {
|
||||
@@ -983,10 +981,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void useIpv6(boolean ipv6Only) throws IOException {
|
||||
private void enableIpv6(boolean enable) throws IOException {
|
||||
if (!state.enableIpv6(enable)) return; // Unchanged
|
||||
try {
|
||||
controlConnection.setConf("ClientUseIPv4", ipv6Only ? "0" : "1");
|
||||
controlConnection.setConf("ClientUseIPv6", ipv6Only ? "1" : "0");
|
||||
controlConnection.setConf("ClientUseIPv4", enable ? "0" : "1");
|
||||
controlConnection.setConf("ClientUseIPv6", enable ? "1" : "0");
|
||||
} catch (TorNotRunningException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -1001,6 +1000,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
stopped = false,
|
||||
networkInitialised = false,
|
||||
networkEnabled = false,
|
||||
paddingEnabled = false,
|
||||
ipv6Enabled = false,
|
||||
bootstrapped = false,
|
||||
circuitBuilt = false,
|
||||
settingsChecked = false;
|
||||
@@ -1015,6 +1016,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
@GuardedBy("this")
|
||||
private int orConnectionsConnected = 0;
|
||||
|
||||
@GuardedBy("this")
|
||||
private List<BridgeType> bridgeTypes = emptyList();
|
||||
|
||||
private synchronized void setStarted() {
|
||||
started = true;
|
||||
callback.pluginStateChanged(getState());
|
||||
@@ -1035,28 +1039,66 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
|
||||
private synchronized void setBootstrapped() {
|
||||
boolean wasBootstrapped = bootstrapped;
|
||||
bootstrapped = true;
|
||||
callback.pluginStateChanged(getState());
|
||||
if (!wasBootstrapped) callback.pluginStateChanged(getState());
|
||||
}
|
||||
|
||||
private synchronized boolean getAndSetCircuitBuilt(boolean built) {
|
||||
boolean old = circuitBuilt;
|
||||
/**
|
||||
* Sets the `circuitBuilt` flag and returns true if the flag has
|
||||
* changed.
|
||||
*/
|
||||
private synchronized boolean setCircuitBuilt(boolean built) {
|
||||
if (built == circuitBuilt) return false; // Unchanged
|
||||
circuitBuilt = built;
|
||||
if (built != old) callback.pluginStateChanged(getState());
|
||||
return old;
|
||||
callback.pluginStateChanged(getState());
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
private synchronized void enableNetwork(boolean enable) {
|
||||
/**
|
||||
* Sets the `networkEnabled` flag and returns true if the flag has
|
||||
* changed.
|
||||
*/
|
||||
private synchronized boolean enableNetwork(boolean enable) {
|
||||
boolean wasInitialised = networkInitialised;
|
||||
boolean wasEnabled = networkEnabled;
|
||||
networkInitialised = true;
|
||||
networkEnabled = enable;
|
||||
if (!enable) circuitBuilt = false;
|
||||
callback.pluginStateChanged(getState());
|
||||
if (!wasInitialised || enable != wasEnabled) {
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
return enable != wasEnabled;
|
||||
}
|
||||
|
||||
private synchronized void setReasonsDisabled(int reasonsDisabled) {
|
||||
/**
|
||||
* Sets the `paddingEnabled` flag and returns true if the flag has
|
||||
* changed. Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean enableConnectionPadding(boolean enable) {
|
||||
if (enable == paddingEnabled) return false; // Unchanged
|
||||
paddingEnabled = enable;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `ipv6Enabled` flag and returns true if the flag has
|
||||
* changed. Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean enableIpv6(boolean enable) {
|
||||
if (enable == ipv6Enabled) return false; // Unchanged
|
||||
ipv6Enabled = enable;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
private synchronized void setReasonsDisabled(int reasons) {
|
||||
boolean wasChecked = settingsChecked;
|
||||
settingsChecked = true;
|
||||
this.reasonsDisabled = reasonsDisabled;
|
||||
callback.pluginStateChanged(getState());
|
||||
int oldReasons = reasonsDisabled;
|
||||
reasonsDisabled = reasons;
|
||||
if (!wasChecked || reasons != oldReasons) {
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
}
|
||||
|
||||
// Doesn't affect getState()
|
||||
@@ -1071,6 +1113,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (serverSocket == ss) serverSocket = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of bridge types being used and returns true if the
|
||||
* list has changed. The list is empty if bridges are disabled.
|
||||
* Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean setBridgeTypes(List<BridgeType> types) {
|
||||
if (types.equals(bridgeTypes)) return false; // Unchanged
|
||||
bridgeTypes = types;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
private synchronized State getState() {
|
||||
if (!started || stopped || !settingsChecked) {
|
||||
return STARTING_STOPPING;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.connection;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
@@ -57,6 +58,10 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase {
|
||||
private final Priority high =
|
||||
new Priority(fromHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
|
||||
|
||||
public ConnectionRegistryImplTest() throws FormatException {
|
||||
// required for throws declaration
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterMultipleConnections() {
|
||||
context.checking(new Expectations() {{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
@@ -47,7 +48,7 @@ public class Blake2bDigestTest extends BrambleTestCase {
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testDigestWithKeyedTestVectors() {
|
||||
public void testDigestWithKeyedTestVectors() throws FormatException {
|
||||
for (String[] keyedTestVector : KEYED_TEST_VECTORS) {
|
||||
byte[] input = StringUtils.fromHexString(keyedTestVector[0]);
|
||||
byte[] key = StringUtils.fromHexString(keyedTestVector[1]);
|
||||
@@ -63,7 +64,8 @@ public class Blake2bDigestTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDigestWithKeyedTestVectorsAndRandomUpdate() {
|
||||
public void testDigestWithKeyedTestVectorsAndRandomUpdate()
|
||||
throws FormatException {
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
for (String[] keyedTestVector : KEYED_TEST_VECTORS) {
|
||||
@@ -138,7 +140,7 @@ public class Blake2bDigestTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runSelfTest() {
|
||||
public void runSelfTest() throws FormatException {
|
||||
Blake2bDigest testDigest = new Blake2bDigest(256);
|
||||
byte[] md = new byte[64];
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
@@ -83,7 +84,7 @@ public class KeyAgreementTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRfc7748TestVector() {
|
||||
public void testRfc7748TestVector() throws FormatException {
|
||||
byte[] aPriv = parsePrivateKey(ALICE_PRIVATE);
|
||||
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
||||
byte[] bPriv = parsePrivateKey(BOB_PRIVATE);
|
||||
@@ -97,7 +98,8 @@ public class KeyAgreementTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDerivesSameSharedSecretFromEquivalentPublicKey() {
|
||||
public void testDerivesSameSharedSecretFromEquivalentPublicKey()
|
||||
throws FormatException {
|
||||
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
||||
byte[] bPriv = parsePrivateKey(BOB_PRIVATE);
|
||||
byte[] sharedSecret = fromHexString(SHARED_SECRET);
|
||||
@@ -168,7 +170,7 @@ public class KeyAgreementTest extends BrambleTestCase {
|
||||
return crypto.deriveSharedSecret(label, publicKey, keyPair, inputs);
|
||||
}
|
||||
|
||||
private byte[] parsePrivateKey(String hex) {
|
||||
private byte[] parsePrivateKey(String hex) throws FormatException {
|
||||
// Private keys need to be clamped because curve25519-java does the
|
||||
// clamping at key generation time, not multiplication time
|
||||
return AgreementKeyParser.clamp(fromHexString(hex));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
@@ -15,11 +16,11 @@ public class XSalsa20Poly1305AuthenticatedCipherTest extends BrambleTestCase {
|
||||
|
||||
// Test vectors from the NaCl paper
|
||||
// http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
|
||||
private static final byte[] TEST_KEY = StringUtils.fromHexString(
|
||||
private final byte[] TEST_KEY = StringUtils.fromHexString(
|
||||
"1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389");
|
||||
private static final byte[] TEST_IV = StringUtils.fromHexString(
|
||||
private final byte[] TEST_IV = StringUtils.fromHexString(
|
||||
"69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37");
|
||||
private static final byte[] TEST_PLAINTEXT = StringUtils.fromHexString(
|
||||
private final byte[] TEST_PLAINTEXT = StringUtils.fromHexString(
|
||||
"be075fc53c81f2d5cf141316" +
|
||||
"ebeb0c7b5228c52a4c62cbd4" +
|
||||
"4b66849b64244ffce5ecbaaf" +
|
||||
@@ -31,7 +32,7 @@ public class XSalsa20Poly1305AuthenticatedCipherTest extends BrambleTestCase {
|
||||
"048977eb48f59ffd4924ca1c" +
|
||||
"60902e52f0a089bc76897040" +
|
||||
"e082f937763848645e0705");
|
||||
private static final byte[] TEST_CIPHERTEXT = StringUtils.fromHexString(
|
||||
private final byte[] TEST_CIPHERTEXT = StringUtils.fromHexString(
|
||||
"f3ffc7703f9400e52a7dfb4b" +
|
||||
"3d3305d98e993b9f48681273" +
|
||||
"c29650ba32fc76ce48332ea7" +
|
||||
@@ -46,6 +47,10 @@ public class XSalsa20Poly1305AuthenticatedCipherTest extends BrambleTestCase {
|
||||
"a43d14a6599b1f654cb45a74" +
|
||||
"e355a5");
|
||||
|
||||
public XSalsa20Poly1305AuthenticatedCipherTest() throws FormatException {
|
||||
// required for throws declaration
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt() throws Exception {
|
||||
SecretKey k = new SecretKey(TEST_KEY);
|
||||
|
||||
@@ -618,11 +618,12 @@ public class BdfReaderImplTest extends BrambleTestCase {
|
||||
r.readDictionary();
|
||||
}
|
||||
|
||||
private void setContents(String hex) {
|
||||
private void setContents(String hex) throws FormatException {
|
||||
setContents(hex, DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
private void setContents(String hex, int maxBufferSize) {
|
||||
private void setContents(String hex, int maxBufferSize)
|
||||
throws FormatException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
|
||||
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
@@ -12,12 +13,10 @@ import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPED;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
||||
@@ -30,6 +29,8 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
private final OpenDatabaseHook hook = context.mock(OpenDatabaseHook.class);
|
||||
private final Service service = context.mock(Service.class);
|
||||
|
||||
private final SecretKey dbKey = getSecretKey();
|
||||
|
||||
@@ -40,8 +41,6 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
public void testOpenDatabaseHooksAreCalledAtStartup() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
Transaction txn = new Transaction(null, false);
|
||||
AtomicBoolean called = new AtomicBoolean(false);
|
||||
OpenDatabaseHook hook = transaction -> called.set(true);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
@@ -50,6 +49,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(false));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(db).removeTemporaryMessages(txn);
|
||||
oneOf(hook).onDatabaseOpened(txn);
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
@@ -57,7 +57,38 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServicesAreStartedAndStopped() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(db).open(dbKey, lifecycleManager);
|
||||
will(returnValue(false));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(db).removeTemporaryMessages(txn);
|
||||
oneOf(service).startService();
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
lifecycleManager.registerService(service);
|
||||
|
||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||
context.assertIsSatisfied();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).close();
|
||||
oneOf(service).stopService();
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
lifecycleManager.stopServices();
|
||||
assertEquals(STOPPED, lifecycleManager.getLifecycleState());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,6 +115,31 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
assertEquals(STARTING, lifecycleManager.getLifecycleState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondCallToStartServicesReturnsEarly() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(db).open(dbKey, lifecycleManager);
|
||||
will(returnValue(false));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(db).removeTemporaryMessages(txn);
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||
context.assertIsSatisfied();
|
||||
|
||||
// Calling startServices() again should not try to open the DB or
|
||||
// start the services again
|
||||
assertEquals(ALREADY_RUNNING, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondCallToStopServicesReturnsEarly() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
@@ -96,7 +152,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(false));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(db).removeTemporaryMessages(txn);
|
||||
exactly(2).of(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||
@@ -104,17 +160,17 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
context.assertIsSatisfied();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
oneOf(db).close();
|
||||
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||
}});
|
||||
|
||||
lifecycleManager.stopServices();
|
||||
assertEquals(STOPPING, lifecycleManager.getLifecycleState());
|
||||
assertEquals(STOPPED, lifecycleManager.getLifecycleState());
|
||||
context.assertIsSatisfied();
|
||||
|
||||
// Calling stopServices() again should not broadcast another event or
|
||||
// try to close the DB again
|
||||
lifecycleManager.stopServices();
|
||||
assertEquals(STOPPING, lifecycleManager.getLifecycleState());
|
||||
assertEquals(STOPPED, lifecycleManager.getLifecycleState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,10 @@ public class MailboxApiTest extends BrambleTestCase {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider);
|
||||
// We aren't using a real onion address, so use the given address verbatim
|
||||
private final UrlConverter urlConverter = onion -> onion;
|
||||
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider,
|
||||
urlConverter);
|
||||
|
||||
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
|
||||
private final MailboxAuthToken token2 = new MailboxAuthToken(getRandomId());
|
||||
|
||||
@@ -49,8 +49,8 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private final static String URL_BASE = "http://127.0.0.1:8000";
|
||||
private final static MailboxAuthToken SETUP_TOKEN;
|
||||
private static final String URL_BASE = "http://127.0.0.1:8000";
|
||||
private static final MailboxAuthToken SETUP_TOKEN;
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -74,8 +74,10 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
private final static MailboxApiImpl api =
|
||||
new MailboxApiImpl(httpClientProvider);
|
||||
// We aren't using a real onion address, so use the given address verbatim
|
||||
private static final UrlConverter urlConverter = onion -> onion;
|
||||
private static final MailboxApiImpl api =
|
||||
new MailboxApiImpl(httpClientProvider, urlConverter);
|
||||
// needs to be static to keep values across different tests
|
||||
private static MailboxProperties ownerProperties;
|
||||
|
||||
@@ -121,7 +123,7 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
ContactId contactId = new ContactId(1);
|
||||
MailboxContact contact = getMailboxContact(contactId);
|
||||
MailboxProperties contactProperties = new MailboxProperties(
|
||||
ownerProperties.getBaseUrl(), contact.token,
|
||||
ownerProperties.getOnion(), contact.token,
|
||||
new ArrayList<>(), contact.inboxId, contact.outboxId);
|
||||
api.addContact(ownerProperties, contact);
|
||||
|
||||
@@ -167,7 +169,7 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
ContactId contactId = new ContactId(1);
|
||||
MailboxContact contact = getMailboxContact(contactId);
|
||||
MailboxProperties contactProperties = new MailboxProperties(
|
||||
ownerProperties.getBaseUrl(), contact.token,
|
||||
ownerProperties.getOnion(), contact.token,
|
||||
new ArrayList<>(), contact.inboxId, contact.outboxId);
|
||||
api.addContact(ownerProperties, contact);
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final String onion = getRandomString(56);
|
||||
private final byte[] onionBytes = getRandomBytes(32);
|
||||
private final String baseUrl = "http://" + onion + ".onion"; // TODO
|
||||
private final MailboxAuthToken setupToken =
|
||||
new MailboxAuthToken(getRandomId());
|
||||
private final MailboxAuthToken ownerToken =
|
||||
@@ -63,9 +62,9 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
private final String validPayload = getValidPayload();
|
||||
private final long time = System.currentTimeMillis();
|
||||
private final MailboxProperties setupProperties = new MailboxProperties(
|
||||
baseUrl, setupToken, new ArrayList<>());
|
||||
onion, setupToken, new ArrayList<>());
|
||||
private final MailboxProperties ownerProperties = new MailboxProperties(
|
||||
baseUrl, ownerToken, new ArrayList<>());
|
||||
onion, ownerToken, new ArrayList<>());
|
||||
|
||||
@Test
|
||||
public void testInitialQrCodeReceivedState() {
|
||||
@@ -207,7 +206,7 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
|
||||
return new PredicateMatcher<>(MailboxProperties.class, p1 ->
|
||||
p1.getAuthToken().equals(p2.getAuthToken()) &&
|
||||
p1.getBaseUrl().equals(p2.getBaseUrl()) &&
|
||||
p1.getOnion().equals(p2.getOnion()) &&
|
||||
p1.isOwner() == p2.isOwner() &&
|
||||
p1.getServerSupports().equals(p2.getServerSupports()));
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
MailboxProperties properties = manager.getOwnMailboxProperties(txn);
|
||||
assertNotNull(properties);
|
||||
assertEquals(onion, properties.getBaseUrl());
|
||||
assertEquals(onion, properties.getOnion());
|
||||
assertEquals(token, properties.getAuthToken());
|
||||
assertEquals(serverSupports, properties.getServerSupports());
|
||||
assertTrue(properties.isOwner());
|
||||
|
||||
@@ -109,7 +109,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
updateNoMailbox = new MailboxUpdate(someClientSupportsList);
|
||||
|
||||
updateProps = getMailboxProperties(false, someServerSupportsList);
|
||||
ownProps = new MailboxProperties(updateProps.getBaseUrl(),
|
||||
ownProps = new MailboxProperties(updateProps.getOnion(),
|
||||
updateProps.getAuthToken(), someServerSupportsList);
|
||||
updateWithMailbox = new MailboxUpdateWithMailbox(someClientSupportsList,
|
||||
updateProps);
|
||||
@@ -170,7 +170,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict, true);
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
sentDict);
|
||||
@@ -225,7 +225,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
someServerSupports, propsDict, true);
|
||||
someServerSupports, propsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
sentDict);
|
||||
@@ -276,7 +276,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict, true);
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
sentDict);
|
||||
@@ -341,7 +341,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict, true);
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
sentDict);
|
||||
@@ -400,7 +400,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2,
|
||||
newerClientSupports, someServerSupports, propsDict, true);
|
||||
newerClientSupports, someServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, messageId);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
@@ -441,7 +441,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict, true);
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
@@ -484,7 +484,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
someServerSupports, propsDict, true);
|
||||
someServerSupports, propsDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
@@ -674,7 +674,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
someServerSupports, propsDict, true);
|
||||
someServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
}});
|
||||
|
||||
@@ -712,7 +712,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId());
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict, true);
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
}});
|
||||
|
||||
@@ -890,15 +890,14 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private void expectStoreMessage(Transaction txn, GroupId g,
|
||||
long version, BdfList clientSupports, BdfList serverSupports,
|
||||
BdfDictionary properties, boolean local)
|
||||
throws Exception {
|
||||
BdfDictionary properties) throws Exception {
|
||||
BdfList body = BdfList.of(version, clientSupports, serverSupports,
|
||||
properties);
|
||||
Message message = getMessage(g);
|
||||
long timestamp = message.getTimestamp();
|
||||
BdfDictionary meta = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, version),
|
||||
new BdfEntry(MSG_KEY_LOCAL, local)
|
||||
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||
);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -11,7 +12,7 @@ import static org.junit.Assert.fail;
|
||||
public class ByteUtilsTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint16() {
|
||||
public void testReadUint16() throws FormatException {
|
||||
byte[] b = StringUtils.fromHexString("00000000");
|
||||
assertEquals(0, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00000100");
|
||||
@@ -33,7 +34,7 @@ public class ByteUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint32() {
|
||||
public void testReadUint32() throws FormatException {
|
||||
byte[] b = StringUtils.fromHexString("000000000000");
|
||||
assertEquals(0, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("000000000100");
|
||||
@@ -55,7 +56,7 @@ public class ByteUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint64() {
|
||||
public void testReadUint64() throws FormatException {
|
||||
byte[] b = StringUtils.fromHexString("00000000000000000000");
|
||||
assertEquals(0L, ByteUtils.readUint64(b, 1));
|
||||
b = StringUtils.fromHexString("00000000000000000100");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -24,18 +25,19 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
assertEquals("", StringUtils.toHexString(new byte[0]));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testFromHexStringRejectsInvalidLength() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromHexStringRejectsInvalidLength() throws FormatException {
|
||||
StringUtils.fromHexString("12345");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testFromHexStringRejectsInvalidCharacter() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFromHexStringRejectsInvalidCharacter()
|
||||
throws FormatException {
|
||||
StringUtils.fromHexString("ABCDEFGH");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexStringUppercase() {
|
||||
public void testFromHexStringUppercase() throws FormatException {
|
||||
String s = "000102037F800A0B0C0D0EFF";
|
||||
byte[] expected = new byte[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x7F, (byte) 0x80,
|
||||
@@ -45,7 +47,7 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexStringLowercase() {
|
||||
public void testFromHexStringLowercase() throws FormatException {
|
||||
String s = "000102037f800a0b0c0d0eff";
|
||||
byte[] expected = new byte[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x7F, (byte) 0x80,
|
||||
@@ -55,7 +57,7 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexStringEmptyInput() {
|
||||
public void testFromHexStringEmptyInput() throws FormatException {
|
||||
assertArrayEquals(new byte[0], StringUtils.fromHexString(""));
|
||||
}
|
||||
|
||||
@@ -174,52 +176,52 @@ public class StringUtilsTest extends BrambleTestCase {
|
||||
assertEquals("", StringUtils.truncateUtf8("", 123));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToBytesRejectsShortMac() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToBytesRejectsShortMac() throws FormatException {
|
||||
StringUtils.macToBytes("00:00:00:00:00");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToBytesRejectsLongMac() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToBytesRejectsLongMac() throws FormatException {
|
||||
StringUtils.macToBytes("00:00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToBytesRejectsInvalidCharacter() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToBytesRejectsInvalidCharacter() throws FormatException {
|
||||
StringUtils.macToBytes("00:00:00:00:00:0g");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToBytesRejectsInvalidFormat() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToBytesRejectsInvalidFormat() throws FormatException {
|
||||
StringUtils.macToBytes("0:000:00:00:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacToBytesUpperCase() {
|
||||
public void testMacToBytesUpperCase() throws FormatException {
|
||||
byte[] expected = new byte[] {0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F};
|
||||
String mac = "0A:1B:2C:3D:4E:5F";
|
||||
assertArrayEquals(expected, StringUtils.macToBytes(mac));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacToBytesLowerCase() {
|
||||
public void testMacToBytesLowerCase() throws FormatException {
|
||||
byte[] expected = new byte[] {0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F};
|
||||
String mac = "0a:1b:2c:3d:4e:5f";
|
||||
assertArrayEquals(expected, StringUtils.macToBytes(mac));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToStringRejectsShortMac() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToStringRejectsShortMac() throws FormatException {
|
||||
StringUtils.macToString(new byte[5]);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMacToStringRejectsLongMac() {
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMacToStringRejectsLongMac() throws FormatException {
|
||||
StringUtils.macToString(new byte[7]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacToString() {
|
||||
public void testMacToString() throws FormatException {
|
||||
byte[] mac = new byte[] {0x0a, 0x1b, 0x2c, 0x3d, 0x4e, 0x5f};
|
||||
String expected = "0A:1B:2C:3D:4E:5F";
|
||||
assertEquals(expected, StringUtils.macToString(mac));
|
||||
|
||||
@@ -26,8 +26,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10409
|
||||
versionName "1.4.9"
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.os.StrictMode;
|
||||
import com.vanniktech.emoji.RecentEmoji;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
@@ -242,7 +243,7 @@ public class AppModule {
|
||||
try {
|
||||
return crypto.getMessageKeyParser().parsePublicKey(
|
||||
StringUtils.fromHexString(DEV_PUBLIC_KEY_HEX));
|
||||
} catch (GeneralSecurityException e) {
|
||||
} catch (GeneralSecurityException | FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +156,11 @@ public class BriarService extends Service {
|
||||
if (result == SUCCESS) {
|
||||
started = true;
|
||||
} else if (result == ALREADY_RUNNING) {
|
||||
LOG.info("Already running");
|
||||
stopSelf();
|
||||
LOG.warning("Already running");
|
||||
// The core has outlived the original BriarService
|
||||
// instance. We don't know how to recover from this
|
||||
// unexpected state, so try to exit cleanly
|
||||
shutdownFromBackground();
|
||||
} else {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Startup failed: " + result);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.widget.Toast;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
@@ -519,8 +520,12 @@ public class ConversationActivity extends BriarActivity
|
||||
Selection<String> selection = tracker.getSelection();
|
||||
List<MessageId> messages = new ArrayList<>(selection.size());
|
||||
for (String str : selection) {
|
||||
MessageId id = new MessageId(fromHexString(str));
|
||||
messages.add(id);
|
||||
try {
|
||||
MessageId id = new MessageId(fromHexString(str));
|
||||
messages.add(id);
|
||||
} catch (FormatException e) {
|
||||
LOG.warning("Invalid message id");
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -109,12 +109,18 @@ public class MailboxActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
private void onShowDownload() {
|
||||
boolean needToShow = true;
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
// if the fragment is already on the back stack, pop back to it
|
||||
// instead of adding it to the stack again
|
||||
if (fm.findFragmentByTag(SetupDownloadFragment.TAG) != null) {
|
||||
// if the fragment is already on the back stack, pop back to it
|
||||
// instead of adding it to the stack again
|
||||
fm.popBackStackImmediate(SetupDownloadFragment.TAG, 0);
|
||||
} else {
|
||||
// if the activity was previously destroyed, the fragment is still
|
||||
// found, but popping back to it won't work, so we need to handle
|
||||
// this case and show the fragment again anyway.
|
||||
needToShow =
|
||||
!fm.popBackStackImmediate(SetupDownloadFragment.TAG, 0);
|
||||
}
|
||||
if (needToShow) {
|
||||
showFragment(fm, new SetupDownloadFragment(),
|
||||
SetupDownloadFragment.TAG);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<string name="dnkm_xiaomi_button">Proteggi Briar</string>
|
||||
<string name="dnkm_xiaomi_help">Se Briar non è fissato nella lista di app recenti, non potrà funzionare in secondo piano.</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_old">1. Apri la lista di app recenti (chiamata anche app switcher)\n\n2. Scorri fino alla schermata di Briar per mostrare l\'icona del lucchetto\n\n3. Se il lucchetto non è chiuso, toccalo per chiuderlo</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_new">1. Apri la lista di app recenti (chiamata anche app switcher)\n\n2. Se Briar ha un piccolo lucchetto accanto al nome, allora non devi fare nulla\n\n3. Se non c\'è un lucchetto, tieni premuta la schermata di Briar finché non compare l\'icona del lucchetto, poi toccala</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_text">Tocca il pulsante sottostante per aprire le impostazioni di sicurezza. Tocca \"Aumenta velocità\", poi \"Blocca le app\" e assicurati che Briar sia impostato su \"Bloccato\".</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_help">Se Briar non è impostato su \"Bloccato\" nella schermata \"Blocca le app\", non riuscirà a funzionare in secondo piano.</string>
|
||||
<string name="dnkm_warning_dozed_1">Briar non è riuscito a funzionare in secondo piano</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Password</string>
|
||||
<string name="try_again">Password sbagliata, riprova</string>
|
||||
@@ -238,6 +242,8 @@
|
||||
<string name="contact_added_toast">Contatto aggiunto: %s</string>
|
||||
<string name="contact_already_exists">Il contatto %s esiste già</string>
|
||||
<string name="qr_code_invalid">Il codice QR non è valido</string>
|
||||
<string name="qr_code_too_old_1">Il codice QR che hai scansionato proviene da una versione più vecchia di Briar.\n\nChiedi al tuo contatto di aggiornare all\'ultima versione e poi riprova.</string>
|
||||
<string name="qr_code_too_new_1">Il codice QR che hai scansionato proviene da una versione più recente di Briar.\n\nAggiorna all\'ultima versione e poi riprova.</string>
|
||||
<string name="camera_error">Errore fotocamera</string>
|
||||
<string name="connecting_to_device">Connessione al dispositivo\u2026</string>
|
||||
<string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string>
|
||||
@@ -613,6 +619,10 @@
|
||||
<string name="mailbox_status_connected_title">Casella postale in esecuzione</string>
|
||||
<string name="mailbox_status_problem_title">Briar sta avendo problemi a connettersi alla casella postale</string>
|
||||
<string name="mailbox_status_failure_title">Casella postale non disponibile</string>
|
||||
<string name="mailbox_status_app_too_old_title">Briar è troppo vecchio</string>
|
||||
<string name="mailbox_status_app_too_old_message">Aggiorna Briar all\'ultima versione e riprova.</string>
|
||||
<string name="mailbox_status_mailbox_too_old_title">La cassella postale è troppo vecchia</string>
|
||||
<string name="mailbox_status_mailbox_too_old_message">Aggiorna la casella postale all\'ultima versione e riprova.</string>
|
||||
<string name="mailbox_status_check_button">Controlla connessione</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Ultima connessione: %s</string>
|
||||
@@ -764,6 +774,9 @@
|
||||
<string name="hotspot_manual_site_address">Indirizzo (URL)</string>
|
||||
<string name="hotspot_qr_site">Il tuo telefono sta fornendo un hotspot Wi-Fi. Le persone connesse all\'hotspot possono scaricare Briar scansionando questo codice QR.</string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">Scarica Briar %s</string>
|
||||
<string name="website_download_intro_1">Qualcuno nelle vicinanze ha condiviso Briar con te.</string>
|
||||
<string name="website_download_button">Scarica Briar</string>
|
||||
<string name="website_download_outro">Dopo il completamento del download, apri il file scaricato e installalo.</string>
|
||||
<string name="website_troubleshooting_title">Risoluzione dei problemi</string>
|
||||
<string name="website_troubleshooting_1">Se non puoi scaricare l\'app, prova con un browser web diverso.</string>
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
<string name="dialog_message_lost_password">Contul dumneavoastră Briar este stocat în mod criptat pe dispozitivul dumneavoastră, nu în cloud, așa că nu vă putem reseta parola. Doriți să vă ștergeți contul și să o luați de la capăt?\n\nAtenție: Identitățile, contactele și mesajele dumneavoastră vor fi pierdute definitiv.</string>
|
||||
<string name="startup_failed_activity_title">Eroare de pornire Briar</string>
|
||||
<string name="startup_failed_clock_error">Briar nu a putut porni deoarece ceasul dispozitivului dvs. este greșit.\n\nVă rugăm să setați ceasul dispozitivului dvs. la ora corectă și să încercați din nou.</string>
|
||||
<string name="startup_failed_db_error">Briar nu a reușit să deschidă baza de date care conține contul dumneavoastră, contactele și mesajele dumneavoastră.\n\nVă rugăm să faceți upgrade la cea mai recentă versiune a app și să încercați din nou, sau să configurați un cont nou alegând \"Mi-am uitat parola\" la solicitarea parolei.</string>
|
||||
<string name="startup_failed_db_error">Briar nu a reușit să deschidă baza de date care conține contul dumneavoastră, contactele și mesajele dumneavoastră.\n\nVă rugăm să faceți upgrade la cea mai recentă versiune a aplicației și să încercați din nou, sau să configurați un cont nou alegând \"Mi-am uitat parola\" la solicitarea parolei.</string>
|
||||
<string name="startup_failed_data_too_old_error">Contul dvs. a fost creat cu o versiune veche a acestei aplicații și nu poate fi deschis cu această versiune.\n\nTrebuie fie să reinstalați versiunea veche, fie să creați un cont nou alegând \"Mi-am uitat parola\" la solicitarea de parolă.</string>
|
||||
<string name="startup_failed_data_too_new_error">Contul dvs. a fost creat cu o versiune mai nouă a acestei aplicații și nu poate fi deschis cu această versiune.\n\nVă rugăm să faceți upgrade la cea mai recentă versiune și să încercați din nou.</string>
|
||||
<string name="startup_failed_service_error">Briar nu a reușit să pornească o componentă necesară.\n\nVă rugăm să faceți upgrade la cea mai recentă versiune a acestui app și să încercați din nou.</string>
|
||||
<string name="startup_failed_service_error">Briar nu a reușit să pornească o componentă necesară.\n\nVă rugăm să faceți upgrade la cea mai recentă versiune a acestei aplicații și să încercați din nou.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">Aceasta este o versiune de test pentru Briar. Contul dumneavoastră va expira în %d zi și nu se poate reînnoi</item>
|
||||
<item quantity="few">Aceasta este o versiune de test pentru Briar. Contul dumneavoastră va expira în %d zile și nu se poate reînnoi.</item>
|
||||
@@ -554,12 +554,12 @@
|
||||
<string name="password_changed">Parola a fost schimbată.</string>
|
||||
<string name="panic_setting">Setare buton de panică</string>
|
||||
<string name="panic_setting_title">Buton de panică</string>
|
||||
<string name="panic_setting_hint">Configurați cum va reacționa Briar atunci când folosiți un app de buton de panică.</string>
|
||||
<string name="panic_app_setting_title">App buton de panică</string>
|
||||
<string name="unknown_app">app necunoscut</string>
|
||||
<string name="panic_app_setting_summary">Nu a fost setată nici un app</string>
|
||||
<string name="panic_setting_hint">Configurați cum va reacționa Briar atunci când folosiți o aplicație de buton de panică.</string>
|
||||
<string name="panic_app_setting_title">Aplicație buton de panică</string>
|
||||
<string name="unknown_app">aplicație necunoscută</string>
|
||||
<string name="panic_app_setting_summary">Nu a fost setată nici o aplicație</string>
|
||||
<string name="panic_app_setting_none">Nici una</string>
|
||||
<string name="dialog_title_connect_panic_app">Confirmare app de panică</string>
|
||||
<string name="dialog_title_connect_panic_app">Confirmare aplicație de panică</string>
|
||||
<string name="dialog_message_connect_panic_app">Sigur doriți să permiteți %1$s să declanșeze acțiuni destructive pentru butonul de panică?</string>
|
||||
<string name="panic_setting_destructive_action">Acțiuni distructive</string>
|
||||
<string name="panic_setting_signout_title">Ieșire</string>
|
||||
@@ -606,7 +606,7 @@
|
||||
<string name="mailbox_setup_io_error_title">Nu s-a putut conecta</string>
|
||||
<string name="mailbox_setup_io_error_description">Asigurați-vă că ambele dispozitive sunt conectate la Internet și încercați din nou.</string>
|
||||
<string name="mailbox_setup_assertion_error_title">Eroare de Cutie poștală</string>
|
||||
<string name="mailbox_setup_assertion_error_description">Vă rugăm să trimiteți feedback (cu date anonime) prin intermediul app-ului Briar dacă problema persistă.</string>
|
||||
<string name="mailbox_setup_assertion_error_description">Vă rugăm să trimiteți feedback (cu date anonime) prin intermediul aplicației Briar dacă problema persistă.</string>
|
||||
<string name="mailbox_setup_camera_error_description">Nu s-a putut accesa camera. Încercați din nou, poate după repornirea dispozitivului.</string>
|
||||
<string name="mailbox_setup_paired_title">Conectat</string>
|
||||
<string name="mailbox_setup_paired_description">Cutia dumneavoastră poștală a fost atașată cu succes la Briar.\n
|
||||
@@ -742,7 +742,7 @@ De asemenea, contactul dvs. poate modifica această setare pentru amândoi.</str
|
||||
<string name="transports_help_text">Briar se poate conecta la contactele dumneavoastră prin Internet, Wi-Fi sau Bluetooth.\n\nToate conexiunile la internet trec prin rețeaua Tor din motive de confidențialitate.\n\nDacă un contact poate fi accesat prin metode multiple, Briar le va folosi în mod paralel.</string>
|
||||
<!--Share app offline-->
|
||||
<string name="hotspot_title">Partajează această aplicație fără Internet</string>
|
||||
<string name="hotspot_intro">Împărtășiți acest app cu o persoană din apropiere fără conexiune la Internet, utilizând Wi-Fi-ul telefonului dumneavoastră.
|
||||
<string name="hotspot_intro">Împărtășiți această aplicație cu o persoană din apropiere fără conexiune la Internet, utilizând Wi-Fi-ul telefonului dumneavoastră.
|
||||
\n\nTelefonul dumneavoastră va porni un hotspot Wi-Fi. Persoanele din apropiere se pot conecta la hotspot și pot descărca aplicația Briar de pe telefonul vostru.</string>
|
||||
<string name="hotspot_button_start_sharing">Pornește hotspot</string>
|
||||
<string name="hotspot_button_stop_sharing">Oprește hotspot</string>
|
||||
@@ -793,7 +793,7 @@ De asemenea, contactul dvs. poate modifica această setare pentru amândoi.</str
|
||||
<string name="hotspot_help_site_4">Dacă puteți vizita siteul dar nu puteți descărca aplicația Briar, încercați cu un alt browser.</string>
|
||||
<string name="hotspot_help_fallback_title">Nimic nu funcționează?</string>
|
||||
<string name="hotspot_help_fallback_intro">Puteți încerca să salvați aplicația ca un fișier .APK pentru a-l partaja în alt mod. Odată fișierul transferat pe celălalt dispozitiv, poate fi folosit să se instaleze Briar.
|
||||
\n\nPont: Pentru a partaja prin Bluetooth s-ar putea sa fie necesar sa redenumiți fișierul ca să aivă terminația .ZIP.</string>
|
||||
\n\nPont: Pentru a partaja prin Bluetooth s-ar putea sa fie necesar sa redenumiți fișierul ca să aibă terminația .ZIP.</string>
|
||||
<string name="hotspot_help_fallback_button">Salvează aplicația</string>
|
||||
<!--error handling-->
|
||||
<string name="hotspot_error_intro">A apărut o problemă când s-a încercat partajarea aplicației prin Wi-Fi.</string>
|
||||
|
||||
Reference in New Issue
Block a user