Compare commits

..

18 Commits

Author SHA1 Message Date
akwizgran
a5563ead28 Bump version numbers for 1.4.10 release. 2022-07-04 16:08:53 +01:00
akwizgran
e15f49fde7 Update translations. 2022-07-04 15:59:57 +01:00
Torsten Grote
1531a24b2d Merge branch '1499-do-not-set-tor-config-during-shutdown' into 'master'
Don't set "DisableNetwork 1" during shutdown

See merge request briar/briar!1684
2022-07-01 13:10:02 +00:00
akwizgran
2298818af5 Don't set "DisableNetwork 1" during shutdown.
This is redundant now that we start from the default config every time.
2022-07-01 12:30:46 +01:00
akwizgran
0ae5361281 Merge branch '1777-lifecycle-manager' into 'master'
Allow process to exit if an exception is thrown during shutdown

Closes #1777

See merge request briar/briar!1668
2022-06-29 14:23:16 +00:00
Torsten Grote
d8e26eebbe Merge branch '1499-do-not-apply-redundant-settings' into 'master'
Start from default Tor config every time, don't apply redundant settings

See merge request briar/briar!1681
2022-06-29 13:50:01 +00:00
akwizgran
692e353046 Convert comments to javadocs. 2022-06-29 13:54:30 +01:00
akwizgran
b9ba7aded5 Merge branch 'string-utils-checked-exceptions' into 'master'
Let StringUtils throw FormatException instead of IllegalArgumentException

See merge request briar/briar!1682
2022-06-29 12:21:29 +00:00
Sebastian Kürten
4bca9decc1 Let StringUtils throw FormatException instead of IllegalArgumentException 2022-06-29 14:13:42 +02:00
akwizgran
7bbe9068bb Start from the default Tor config every time.
Don't apply settings to Tor unless they've changed.
2022-06-28 12:42:55 +01:00
akwizgran
63060679a3 Merge branch '2342-mailbox-setup-stuck' into 'master'
Ensure that mailbox setup can proceed after activity was destroyed

Closes #2342

See merge request briar/briar!1678
2022-06-26 13:04:34 +00:00
Torsten Grote
ddb759dbb8 Merge branch 'mailbox-base-url-refactoring' into 'master'
Refactor MailboxProperties to hold raw onion address

See merge request briar/briar!1680
2022-06-23 17:27:17 +00:00
akwizgran
feb8854678 Add @Inject constructor. 2022-06-23 14:22:25 +01:00
Torsten Grote
1af52b21d5 Ensure that mailbox setup can proceed after activity was destroyed 2022-06-22 11:44:07 -03:00
akwizgran
e481a02126 Shutdown from background if BriarService is recreated. 2022-06-09 18:10:24 +01:00
akwizgran
825dff27fc Exit if BriarService finds lifecycle already running. 2022-06-09 18:06:08 +01:00
akwizgran
de3a87fff5 Return early when starting/stopping if not in expected state. 2022-06-09 18:01:32 +01:00
akwizgran
85d1addd04 Continue shutdown if an exception is thrown. 2022-06-09 17:16:02 +01:00
40 changed files with 479 additions and 246 deletions

View File

@@ -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"

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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(':');

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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<>());
}
}

View File

@@ -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);

View File

@@ -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()),

View File

@@ -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);
}

View File

@@ -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";
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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() {{

View File

@@ -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];

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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());
}
}

View File

@@ -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());

View File

@@ -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);

View File

@@ -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()));
}

View File

@@ -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());

View File

@@ -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() {{

View File

@@ -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");

View File

@@ -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));

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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>

View File

@@ -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>